Edit

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

Branch :

  • Show log

    Commit

  • Author : Xinghua Cao
    Date : 2021-03-29 17:18:52
    Hash : 80305ca4
    Message : D3D: fix uniform block alignment error If the uniform block will be translated to HLSL StructuredBuffer, in order to follow the std140 rules, we should add padding between the members if necessary. Bug: chromium:1193170 Change-Id: I75bc3538a66fc0c2a9c9ebf15fddeaab94d7a949 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2790518 Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jiajia Qin <jiajia.qin@intel.com> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Xinghua Cao <xinghua.cao@intel.com>

  • src/tests/gl_tests/UniformBufferTest.cpp
  • //
    // Copyright 2015 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.
    //
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    #include "util/random_utils.h"
    
    using namespace angle;
    
    namespace
    {
    
    class UniformBufferTest : public ANGLETest
    {
      protected:
        UniformBufferTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        void testSetUp() override
        {
            mkFS = R"(#version 300 es
    precision highp float;
    uniform uni { vec4 color; };
    out vec4 fragColor;
    void main()
    {
        fragColor = color;
    })";
    
            mProgram = CompileProgram(essl3_shaders::vs::Simple(), mkFS);
            ASSERT_NE(mProgram, 0u);
    
            mUniformBufferIndex = glGetUniformBlockIndex(mProgram, "uni");
            ASSERT_NE(mUniformBufferIndex, -1);
    
            glGenBuffers(1, &mUniformBuffer);
    
            ASSERT_GL_NO_ERROR();
        }
    
        void testTearDown() override
        {
            glDeleteBuffers(1, &mUniformBuffer);
            glDeleteProgram(mProgram);
        }
    
        const char *mkFS;
        GLuint mProgram;
        GLint mUniformBufferIndex;
        GLuint mUniformBuffer;
    };
    
    // Basic UBO functionality.
    TEST_P(UniformBufferTest, Simple)
    {
        glClear(GL_COLOR_BUFFER_BIT);
        float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f};
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 4, floatData, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
    
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1);
    }
    
    // Test that using a UBO with a non-zero offset and size actually works.
    // The first step of this test renders a color from a UBO with a zero offset.
    // The second step renders a color from a UBO with a non-zero offset.
    TEST_P(UniformBufferTest, UniformBufferRange)
    {
        int px = getWindowWidth() / 2;
        int py = getWindowHeight() / 2;
    
        // Query the uniform buffer alignment requirement
        GLint alignment;
        glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment);
    
        GLint64 maxUniformBlockSize;
        glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
        if (alignment >= maxUniformBlockSize)
        {
            // ANGLE doesn't implement UBO offsets for this platform.
            // Ignore the test case.
            return;
        }
    
        ASSERT_GL_NO_ERROR();
    
        // Let's create a buffer which contains two vec4.
        GLuint vec4Size = 4 * sizeof(float);
        GLuint stride   = 0;
        do
        {
            stride += alignment;
        } while (stride < vec4Size);
    
        std::vector<char> v(2 * stride);
        float *first  = reinterpret_cast<float *>(v.data());
        float *second = reinterpret_cast<float *>(v.data() + stride);
    
        first[0] = 10.f / 255.f;
        first[1] = 20.f / 255.f;
        first[2] = 30.f / 255.f;
        first[3] = 40.f / 255.f;
    
        second[0] = 110.f / 255.f;
        second[1] = 120.f / 255.f;
        second[2] = 130.f / 255.f;
        second[3] = 140.f / 255.f;
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        // We use on purpose a size which is not a multiple of the alignment.
        glBufferData(GL_UNIFORM_BUFFER, stride + vec4Size, v.data(), GL_STATIC_DRAW);
    
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
    
        EXPECT_GL_NO_ERROR();
    
        // Bind the first part of the uniform buffer and draw
        // Use a size which is smaller than the alignment to check
        // to check that this case is handle correctly in the conversion to 11.1.
        glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, 0, vec4Size);
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
    
        // Bind the second part of the uniform buffer and draw
        // Furthermore the D3D11.1 backend will internally round the vec4Size (16 bytes) to a stride
        // (256 bytes) hence it will try to map the range [stride, 2 * stride] which is out-of-bound of
        // the buffer bufferSize = stride + vec4Size < 2 * stride. Ensure that this behaviour works.
        glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, stride, vec4Size);
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(px, py, 110, 120, 130, 140);
    }
    
    // Test uniform block bindings.
    TEST_P(UniformBufferTest, UniformBufferBindings)
    {
        int px = getWindowWidth() / 2;
        int py = getWindowHeight() / 2;
    
        ASSERT_GL_NO_ERROR();
    
        // Let's create a buffer which contains one vec4.
        GLuint vec4Size = 4 * sizeof(float);
        std::vector<char> v(vec4Size);
        float *first = reinterpret_cast<float *>(v.data());
    
        first[0] = 10.f / 255.f;
        first[1] = 20.f / 255.f;
        first[2] = 30.f / 255.f;
        first[3] = 40.f / 255.f;
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
    
        EXPECT_GL_NO_ERROR();
    
        // Try to bind the buffer to binding point 2
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 2);
        glBindBufferBase(GL_UNIFORM_BUFFER, 2, mUniformBuffer);
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
    
        // Clear the framebuffer
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_PIXEL_EQ(px, py, 0, 0, 0, 0);
    
        // Try to bind the buffer to another binding point
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 5);
        glBindBufferBase(GL_UNIFORM_BUFFER, 5, mUniformBuffer);
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
    }
    
    // Test that ANGLE handles used but unbound UBO. Assumes we are running on ANGLE and produce
    // optional but not mandatory errors.
    TEST_P(UniformBufferTest, ANGLEUnboundUniformBuffer)
    {
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    }
    
    // Update a UBO many time and verify that ANGLE uses the latest version of the data.
    // https://code.google.com/p/angleproject/issues/detail?id=965
    TEST_P(UniformBufferTest, UniformBufferManyUpdates)
    {
        // TODO(jmadill): Figure out why this fails on OSX Intel OpenGL.
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX() && IsOpenGL());
    
        int px = getWindowWidth() / 2;
        int py = getWindowHeight() / 2;
    
        ASSERT_GL_NO_ERROR();
    
        float data[4];
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(data), nullptr, GL_DYNAMIC_DRAW);
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
    
        EXPECT_GL_NO_ERROR();
    
        // Repeteadly update the data and draw
        for (size_t i = 0; i < 10; ++i)
        {
            data[0] = (i + 10.f) / 255.f;
            data[1] = (i + 20.f) / 255.f;
            data[2] = (i + 30.f) / 255.f;
            data[3] = (i + 40.f) / 255.f;
    
            glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data), data);
    
            drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
            EXPECT_GL_NO_ERROR();
            EXPECT_PIXEL_EQ(px, py, i + 10, i + 20, i + 30, i + 40);
        }
    }
    
    // Use a large number of buffer ranges (compared to the actual size of the UBO)
    TEST_P(UniformBufferTest, ManyUniformBufferRange)
    {
        int px = getWindowWidth() / 2;
        int py = getWindowHeight() / 2;
    
        // Query the uniform buffer alignment requirement
        GLint alignment;
        glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment);
    
        GLint64 maxUniformBlockSize;
        glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
        if (alignment >= maxUniformBlockSize)
        {
            // ANGLE doesn't implement UBO offsets for this platform.
            // Ignore the test case.
            return;
        }
    
        ASSERT_GL_NO_ERROR();
    
        // Let's create a buffer which contains eight vec4.
        GLuint vec4Size = 4 * sizeof(float);
        GLuint stride   = 0;
        do
        {
            stride += alignment;
        } while (stride < vec4Size);
    
        std::vector<char> v(8 * stride);
    
        for (size_t i = 0; i < 8; ++i)
        {
            float *data = reinterpret_cast<float *>(v.data() + i * stride);
    
            data[0] = (i + 10.f) / 255.f;
            data[1] = (i + 20.f) / 255.f;
            data[2] = (i + 30.f) / 255.f;
            data[3] = (i + 40.f) / 255.f;
        }
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, v.size(), v.data(), GL_STATIC_DRAW);
    
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
    
        EXPECT_GL_NO_ERROR();
    
        // Bind each possible offset
        for (size_t i = 0; i < 8; ++i)
        {
            glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, i * stride, stride);
            drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
            EXPECT_GL_NO_ERROR();
            EXPECT_PIXEL_EQ(px, py, 10 + i, 20 + i, 30 + i, 40 + i);
        }
    
        // Try to bind larger range
        for (size_t i = 0; i < 7; ++i)
        {
            glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, i * stride, 2 * stride);
            drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
            EXPECT_GL_NO_ERROR();
            EXPECT_PIXEL_EQ(px, py, 10 + i, 20 + i, 30 + i, 40 + i);
        }
    
        // Try to bind even larger range
        for (size_t i = 0; i < 5; ++i)
        {
            glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, i * stride, 4 * stride);
            drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
            EXPECT_GL_NO_ERROR();
            EXPECT_PIXEL_EQ(px, py, 10 + i, 20 + i, 30 + i, 40 + i);
        }
    }
    
    // Tests that active uniforms have the right names.
    TEST_P(UniformBufferTest, ActiveUniformNames)
    {
        constexpr char kVS[] =
            "#version 300 es\n"
            "in vec2 position;\n"
            "out vec2 v;\n"
            "uniform blockName1 {\n"
            "  float f1;\n"
            "} instanceName1;\n"
            "uniform blockName2 {\n"
            "  float f2;\n"
            "} instanceName2[1];\n"
            "void main() {\n"
            "  v = vec2(instanceName1.f1, instanceName2[0].f2);\n"
            "  gl_Position = vec4(position, 0, 1);\n"
            "}";
    
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision highp float;\n"
            "in vec2 v;\n"
            "out vec4 color;\n"
            "void main() {\n"
            "  color = vec4(v, 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
    
        GLint activeUniformBlocks;
        glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks);
        ASSERT_EQ(2, activeUniformBlocks);
    
        GLuint index = glGetUniformBlockIndex(program, "blockName1");
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
    
        index = glGetUniformBlockIndex(program, "blockName2[0]");
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
    
        GLint activeUniforms;
        glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms);
    
        ASSERT_EQ(2, activeUniforms);
    
        GLint size;
        GLenum type;
        GLint maxLength;
        GLsizei length;
    
        glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
        std::vector<GLchar> strUniformNameBuffer(maxLength + 1, 0);
        const GLchar *uniformNames[1];
        uniformNames[0] = "blockName1.f1";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program, index, maxLength, &length, &size, &type, &strUniformNameBuffer[0]);
        EXPECT_EQ(1, size);
        EXPECT_GLENUM_EQ(GL_FLOAT, type);
        EXPECT_EQ("blockName1.f1", std::string(&strUniformNameBuffer[0]));
    
        uniformNames[0] = "blockName2.f2";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program, index, maxLength, &length, &size, &type, &strUniformNameBuffer[0]);
        EXPECT_EQ(1, size);
        EXPECT_GLENUM_EQ(GL_FLOAT, type);
        EXPECT_EQ("blockName2.f2", std::string(&strUniformNameBuffer[0]));
    }
    
    // Tests active uniforms and blocks when the layout is std140, shared and packed.
    TEST_P(UniformBufferTest, ActiveUniformNumberAndName)
    {
        constexpr char kVS[] =
            "#version 300 es\n"
            "in vec2 position;\n"
            "out float v;\n"
            "struct S {\n"
            "  highp ivec3 a;\n"
            "  mediump ivec2 b[4];\n"
            "};\n"
            "layout(std140) uniform blockName0 {\n"
            "  S s0;\n"
            "  lowp vec2 v0;\n"
            "  S s1[2];\n"
            "  highp uint u0;\n"
            "};\n"
            "layout(std140) uniform blockName1 {\n"
            "  float f1;\n"
            "  bool b1;\n"
            "} instanceName1;\n"
            "layout(shared) uniform blockName2 {\n"
            "  float f2;\n"
            "};\n"
            "layout(packed) uniform blockName3 {\n"
            "  float f3;\n"
            "};\n"
            "void main() {\n"
            "  v = instanceName1.f1;\n"
            "  gl_Position = vec4(position, 0, 1);\n"
            "}";
    
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision highp float;\n"
            "in float v;\n"
            "out vec4 color;\n"
            "void main() {\n"
            "  color = vec4(v, 0, 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
    
        // Note that the packed |blockName3| might (or might not) be optimized out.
        GLint activeUniforms;
        glGetProgramiv(program.get(), GL_ACTIVE_UNIFORMS, &activeUniforms);
        EXPECT_GE(activeUniforms, 11);
    
        GLint activeUniformBlocks;
        glGetProgramiv(program.get(), GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks);
        EXPECT_GE(activeUniformBlocks, 3);
    
        GLint maxLength, size;
        GLenum type;
        GLsizei length;
        GLuint index;
        const GLchar *uniformNames[1];
        glGetProgramiv(program.get(), GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
        std::vector<GLchar> strBuffer(maxLength + 1, 0);
    
        uniformNames[0] = "s0.a";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        EXPECT_EQ(1, size);
        EXPECT_EQ("s0.a", std::string(&strBuffer[0]));
    
        uniformNames[0] = "s0.b[0]";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(4, size);
        EXPECT_EQ("s0.b[0]", std::string(&strBuffer[0]));
    
        uniformNames[0] = "v0";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, size);
        EXPECT_EQ("v0", std::string(&strBuffer[0]));
    
        uniformNames[0] = "s1[0].a";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, size);
        EXPECT_EQ("s1[0].a", std::string(&strBuffer[0]));
    
        uniformNames[0] = "s1[0].b[0]";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(4, size);
        EXPECT_EQ("s1[0].b[0]", std::string(&strBuffer[0]));
    
        uniformNames[0] = "s1[1].a";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, size);
        EXPECT_EQ("s1[1].a", std::string(&strBuffer[0]));
    
        uniformNames[0] = "s1[1].b[0]";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(4, size);
        EXPECT_EQ("s1[1].b[0]", std::string(&strBuffer[0]));
    
        uniformNames[0] = "u0";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, size);
        EXPECT_EQ("u0", std::string(&strBuffer[0]));
    
        uniformNames[0] = "blockName1.f1";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, size);
        EXPECT_EQ("blockName1.f1", std::string(&strBuffer[0]));
    
        uniformNames[0] = "blockName1.b1";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, size);
        EXPECT_EQ("blockName1.b1", std::string(&strBuffer[0]));
    
        uniformNames[0] = "f2";
        glGetUniformIndices(program, 1, uniformNames, &index);
        EXPECT_NE(GL_INVALID_INDEX, index);
        ASSERT_GL_NO_ERROR();
        glGetActiveUniform(program.get(), index, maxLength, &length, &size, &type, &strBuffer[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, size);
        EXPECT_EQ("f2", std::string(&strBuffer[0]));
    }
    
    // Test that using a very large buffer to back a small uniform block works OK.
    TEST_P(UniformBufferTest, VeryLarge)
    {
        glClear(GL_COLOR_BUFFER_BIT);
        float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f};
    
        GLsizei bigSize = 4096 * 64;
        std::vector<GLubyte> zero(bigSize, 0);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, bigSize, zero.data(), GL_STATIC_DRAW);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(float) * 4, floatData);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
    
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1);
    }
    
    // Test that readback from a very large uniform buffer works OK.
    TEST_P(UniformBufferTest, VeryLargeReadback)
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Generate some random data.
        GLsizei bigSize = 4096 * 64;
        std::vector<GLubyte> expectedData(bigSize);
        for (GLsizei index = 0; index < bigSize; ++index)
        {
            expectedData[index] = static_cast<GLubyte>(index);
        }
    
        // Initialize the GL buffer.
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, bigSize, expectedData.data(), GL_STATIC_DRAW);
    
        // Do a small update.
        GLsizei smallSize              = sizeof(float) * 4;
        std::array<float, 4> floatData = {{0.5f, 0.75f, 0.25f, 1.0f}};
        memcpy(expectedData.data(), floatData.data(), smallSize);
    
        glBufferSubData(GL_UNIFORM_BUFFER, 0, smallSize, expectedData.data());
    
        // Draw with the buffer.
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
        drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1);
    
        // Read back the large buffer data.
        const void *mapPtr = glMapBufferRange(GL_UNIFORM_BUFFER, 0, bigSize, GL_MAP_READ_BIT);
        ASSERT_GL_NO_ERROR();
        const GLubyte *bytePtr = reinterpret_cast<const GLubyte *>(mapPtr);
        std::vector<GLubyte> actualData(bytePtr, bytePtr + bigSize);
        EXPECT_EQ(expectedData, actualData);
    
        glUnmapBuffer(GL_UNIFORM_BUFFER);
    }
    
    class UniformBufferTest31 : public ANGLETest
    {
      protected:
        UniformBufferTest31()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    };
    
    // Test uniform block bindings greater than GL_MAX_UNIFORM_BUFFER_BINDINGS cause compile error.
    TEST_P(UniformBufferTest31, MaxUniformBufferBindingsExceeded)
    {
        GLint maxUniformBufferBindings;
        glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);
        std::string source =
            "#version 310 es\n"
            "in vec4 position;\n"
            "layout(binding = ";
        std::stringstream ss;
        ss << maxUniformBufferBindings;
        source = source + ss.str() +
                 ") uniform uni {\n"
                 "    vec4 color;\n"
                 "};\n"
                 "void main()\n"
                 "{\n"
                 "    gl_Position = position;\n"
                 "}";
        GLuint shader = CompileShader(GL_VERTEX_SHADER, source.c_str());
        EXPECT_EQ(0u, shader);
    }
    
    // Test uniform block bindings specified by layout in shader work properly.
    TEST_P(UniformBufferTest31, UniformBufferBindings)
    {
        constexpr char kVS[] =
            "#version 310 es\n"
            "in vec4 position;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = position;\n"
            "}";
        constexpr char kFS[] =
            "#version 310 es\n"
            "precision highp float;\n"
            "layout(binding = 2) uniform uni {\n"
            "    vec4 color;\n"
            "};\n"
            "out vec4 fragColor;\n"
            "void main()\n"
            "{"
            "    fragColor = color;\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        GLuint uniformBufferIndex = glGetUniformBlockIndex(program, "uni");
        ASSERT_NE(GL_INVALID_INDEX, uniformBufferIndex);
        GLBuffer uniformBuffer;
    
        int px = getWindowWidth() / 2;
        int py = getWindowHeight() / 2;
    
        ASSERT_GL_NO_ERROR();
    
        // Let's create a buffer which contains one vec4.
        GLuint vec4Size = 4 * sizeof(float);
        std::vector<char> v(vec4Size);
        float *first = reinterpret_cast<float *>(v.data());
    
        first[0] = 10.f / 255.f;
        first[1] = 20.f / 255.f;
        first[2] = 30.f / 255.f;
        first[3] = 40.f / 255.f;
    
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer.get());
        glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
    
        EXPECT_GL_NO_ERROR();
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 2, uniformBuffer.get());
        drawQuad(program, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
    
        // Clear the framebuffer
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_PIXEL_EQ(px, py, 0, 0, 0, 0);
    
        // Try to bind the buffer to another binding point
        glUniformBlockBinding(program, uniformBufferIndex, 5);
        glBindBufferBase(GL_UNIFORM_BUFFER, 5, uniformBuffer.get());
        drawQuad(program, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
    }
    
    // Test uniform blocks used as instanced array take next binding point for each subsequent element.
    TEST_P(UniformBufferTest31, ConsecutiveBindingsForBlockArray)
    {
        constexpr char kFS[] =
            "#version 310 es\n"
            "precision highp float;\n"
            "layout(binding = 2) uniform uni {\n"
            "    vec4 color;\n"
            "} blocks[2];\n"
            "out vec4 fragColor;\n"
            "void main()\n"
            "{\n"
            "    fragColor = blocks[0].color + blocks[1].color;\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS);
        std::array<GLBuffer, 2> uniformBuffers;
    
        int px = getWindowWidth() / 2;
        int py = getWindowHeight() / 2;
    
        ASSERT_GL_NO_ERROR();
    
        // Let's create a buffer which contains one vec4.
        GLuint vec4Size = 4 * sizeof(float);
        std::vector<char> v(vec4Size);
        float *first = reinterpret_cast<float *>(v.data());
    
        first[0] = 10.f / 255.f;
        first[1] = 20.f / 255.f;
        first[2] = 30.f / 255.f;
        first[3] = 40.f / 255.f;
    
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffers[0].get());
        glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
        EXPECT_GL_NO_ERROR();
        glBindBufferBase(GL_UNIFORM_BUFFER, 2, uniformBuffers[0].get());
        ASSERT_GL_NO_ERROR();
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffers[1].get());
        glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
        EXPECT_GL_NO_ERROR();
        glBindBufferBase(GL_UNIFORM_BUFFER, 3, uniformBuffers[1].get());
    
        drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(px, py, 20, 40, 60, 80);
    }
    
    // Test the layout qualifier binding must be both specified(ESSL 3.10.4 section 9.2).
    TEST_P(UniformBufferTest31, BindingMustBeBothSpecified)
    {
        constexpr char kVS[] =
            "#version 310 es\n"
            "in vec4 position;\n"
            "uniform uni\n"
            "{\n"
            "    vec4 color;\n"
            "} block;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = position + block.color;\n"
            "}";
        constexpr char kFS[] =
            "#version 310 es\n"
            "precision highp float;\n"
            "layout(binding = 0) uniform uni\n"
            "{\n"
            "    vec4 color;\n"
            "} block;\n"
            "out vec4 fragColor;\n"
            "void main()\n"
            "{\n"
            "    fragColor = block.color;\n"
            "}";
        GLuint program = CompileProgram(kVS, kFS);
        ASSERT_EQ(0u, program);
    }
    
    // Test with a block containing an array of structs.
    TEST_P(UniformBufferTest, BlockContainingArrayOfStructs)
    {
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "struct light_t {\n"
            "    vec4 intensity;\n"
            "};\n"
            "const int maxLights = 2;\n"
            "layout(std140) uniform lightData { light_t lights[maxLights]; };\n"
            "vec4 processLight(vec4 lighting, light_t light)\n"
            "{\n"
            "    return lighting + light.intensity;\n"
            "}\n"
            "void main()\n"
            "{\n"
            "    vec4 lighting = vec4(0, 0, 0, 1);\n"
            "    for (int n = 0; n < maxLights; n++)\n"
            "    {\n"
            "        lighting = processLight(lighting, lights[n]);\n"
            "    }\n"
            "    my_FragColor = lighting;\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "lightData");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kStructCount        = 2;
        const GLsizei kVectorElementCount = 4;
        const GLsizei kBytesPerElement    = 4;
        const GLsizei kDataSize           = kStructCount * kVectorElementCount * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[1]                       = 0.5f;
        vAsFloat[kVectorElementCount + 1] = 0.5f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test with a block instance array containing an array of structs.
    TEST_P(UniformBufferTest, BlockArrayContainingArrayOfStructs)
    {
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
            struct light_t
            {
                vec4 intensity;
            };
    
            layout(std140) uniform lightData { light_t lights[2]; } buffers[2];
    
            vec4 processLight(vec4 lighting, light_t light)
            {
                return lighting + light.intensity;
            }
            void main()
            {
                vec4 lighting = vec4(0, 0, 0, 1);
                lighting = processLight(lighting, buffers[0].lights[0]);
                lighting = processLight(lighting, buffers[1].lights[1]);
                my_FragColor = lighting;
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex  = glGetUniformBlockIndex(program, "lightData[0]");
        GLint uniformBuffer2Index = glGetUniformBlockIndex(program, "lightData[1]");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kStructCount        = 2;
        const GLsizei kVectorElementCount = 4;
        const GLsizei kBytesPerElement    = 4;
        const GLsizei kDataSize           = kStructCount * kVectorElementCount * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        // In the first struct/vector of the first block
        vAsFloat[1] = 0.5f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
    
        GLBuffer uniformBuffer2;
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer2);
    
        vAsFloat[1] = 0.0f;
        // In the second struct/vector of the second block
        vAsFloat[kVectorElementCount + 1] = 0.5f;
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glBindBufferBase(GL_UNIFORM_BUFFER, 1, uniformBuffer2);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        glUniformBlockBinding(program, uniformBuffer2Index, 1);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test with a block containing an array of structs containing arrays.
    TEST_P(UniformBufferTest, BlockContainingArrayOfStructsContainingArrays)
    {
        constexpr char kFS[] =
            R"(#version 300 es
            precision highp float;
            out vec4 my_FragColor;
            struct light_t
            {
                vec4 intensity[3];
            };
            const int maxLights = 2;
            layout(std140) uniform lightData { light_t lights[maxLights]; };
            vec4 processLight(vec4 lighting, light_t light)
            {
                return lighting + light.intensity[1];
            }
            void main()
            {
                vec4 lighting = vec4(0, 0, 0, 1);
                lighting = processLight(lighting, lights[0]);
                lighting = processLight(lighting, lights[1]);
                my_FragColor = lighting;
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "lightData");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kStructCount       = 2;
        const GLsizei kVectorsPerStruct  = 3;
        const GLsizei kElementsPerVector = 4;
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize =
            kStructCount * kVectorsPerStruct * kElementsPerVector * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[kElementsPerVector + 1]                                          = 0.5f;
        vAsFloat[kVectorsPerStruct * kElementsPerVector + kElementsPerVector + 1] = 0.5f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test with a block containing nested structs.
    TEST_P(UniformBufferTest, BlockContainingNestedStructs)
    {
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "struct light_t {\n"
            "    vec4 intensity;\n"
            "};\n"
            "struct lightWrapper_t {\n"
            "    light_t light;\n"
            "};\n"
            "const int maxLights = 2;\n"
            "layout(std140) uniform lightData { lightWrapper_t lightWrapper; };\n"
            "vec4 processLight(vec4 lighting, lightWrapper_t aLightWrapper)\n"
            "{\n"
            "    return lighting + aLightWrapper.light.intensity;\n"
            "}\n"
            "void main()\n"
            "{\n"
            "    vec4 lighting = vec4(0, 0, 0, 1);\n"
            "    for (int n = 0; n < maxLights; n++)\n"
            "    {\n"
            "        lighting = processLight(lighting, lightWrapper);\n"
            "    }\n"
            "    my_FragColor = lighting;\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "lightData");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kVectorsPerStruct  = 3;
        const GLsizei kElementsPerVector = 4;
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kVectorsPerStruct * kElementsPerVector * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[1] = 1.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Tests GetUniformBlockIndex return value on error.
    TEST_P(UniformBufferTest, GetUniformBlockIndexDefaultReturn)
    {
        ASSERT_FALSE(glIsProgram(99));
        EXPECT_EQ(GL_INVALID_INDEX, glGetUniformBlockIndex(99, "farts"));
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    }
    
    // Block names can be reserved names in GLSL, as long as they're not reserved in GLSL ES.
    TEST_P(UniformBufferTest, UniformBlockReservedOpenGLName)
    {
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform buffer { vec4 color; };\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = color;\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kElementsPerVector = 4;
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kElementsPerVector * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[1] = 1.0f;
        vAsFloat[3] = 1.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Block instance names can be reserved names in GLSL, as long as they're not reserved in GLSL ES.
    TEST_P(UniformBufferTest, UniformBlockInstanceReservedOpenGLName)
    {
        constexpr char kFS[] =
            "#version 300 es\n"
            "precision highp float;\n"
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform dmat2 { vec4 color; } buffer;\n"
            "void main()\n"
            "{\n"
            "    my_FragColor = buffer.color;\n"
            "}\n";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "dmat2");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kElementsPerVector = 4;
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kElementsPerVector * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[1] = 1.0f;
        vAsFloat[3] = 1.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Test that uniform block instance with nested structs that contain vec3s inside is handled
    // correctly. This is meant to test that HLSL structure padding to implement std140 layout works
    // together with uniform blocks.
    TEST_P(UniformBufferTest, Std140UniformBlockInstanceWithNestedStructsContainingVec3s)
    {
        // Got incorrect test result on non-NVIDIA Android - the alpha channel was not set correctly
        // from the second vector, possibly the platform doesn't implement std140 packing right?
        // http://anglebug.com/2217
        ANGLE_SKIP_TEST_IF(IsAndroid() && !IsNVIDIA());
    
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
    
            struct Sinner {
              vec3 v;
            };
    
            struct S {
                Sinner s1;
                Sinner s2;
            };
    
            layout(std140) uniform structBuffer { S s; } buffer;
    
            void accessStruct(S s)
            {
                my_FragColor = vec4(s.s1.v.xy, s.s2.v.xy);
            }
    
            void main()
            {
                accessStruct(buffer.s);
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "structBuffer");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kVectorsPerBlock         = 2;
        const GLsizei kElementsPerPaddedVector = 4;
        const GLsizei kBytesPerElement         = 4;
        const GLsizei kDataSize = kVectorsPerBlock * kElementsPerPaddedVector * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        // Set second value in each vec3.
        vAsFloat[1u]      = 1.0f;
        vAsFloat[4u + 1u] = 1.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Tests the detaching shaders from the program and using uniform blocks works.
    // This covers a bug in ANGLE's D3D back-end.
    TEST_P(UniformBufferTest, DetachShaders)
    {
        GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple());
        ASSERT_NE(0u, vertexShader);
        GLuint kFS = CompileShader(GL_FRAGMENT_SHADER, mkFS);
        ASSERT_NE(0u, kFS);
    
        GLuint program = glCreateProgram();
        glAttachShader(program, vertexShader);
        glAttachShader(program, kFS);
    
        ASSERT_TRUE(LinkAttachedProgram(program));
    
        glDetachShader(program, vertexShader);
        glDetachShader(program, kFS);
        glDeleteShader(vertexShader);
        glDeleteShader(kFS);
    
        glClear(GL_COLOR_BUFFER_BIT);
        float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f};
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 4, floatData, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
    
        GLint uniformBufferIndex = glGetUniformBlockIndex(mProgram, "uni");
        ASSERT_NE(uniformBufferIndex, -1);
    
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1);
    
        glDeleteProgram(program);
    }
    
    // Test a uniform block where the whole block is set as row-major.
    TEST_P(UniformBufferTest, Std140UniformBlockWithRowMajorQualifier)
    {
        // AMD OpenGL driver doesn't seem to apply the row-major qualifier right.
        // http://anglebug.com/2273
        ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL() && !IsOSX());
    
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
    
            layout(std140, row_major) uniform matrixBuffer
            {
                mat2 m;
            } buffer;
    
            void main()
            {
                // Vector constructor accesses elements in column-major order.
                my_FragColor = vec4(buffer.m);
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "matrixBuffer");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kElementsPerMatrix = 8;  // Each mat2 row gets padded into a vec4.
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kElementsPerMatrix * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[0u] = 1.0f;
        vAsFloat[1u] = 128.0f / 255.0f;
        vAsFloat[4u] = 64.0f / 255.0f;
        vAsFloat[5u] = 32.0f / 255.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 64, 128, 32), 5);
    }
    
    // Test a uniform block where an individual matrix field is set as row-major whereas the whole block
    // is set as column-major.
    TEST_P(UniformBufferTest, Std140UniformBlockWithPerMemberRowMajorQualifier)
    {
        // AMD OpenGL driver doesn't seem to apply the row-major qualifier right.
        // http://anglebug.com/2273
        ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL() && !IsOSX());
    
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
    
            layout(std140, column_major) uniform matrixBuffer
            {
                layout(row_major) mat2 m;
            } buffer;
    
            void main()
            {
                // Vector constructor accesses elements in column-major order.
                my_FragColor = vec4(buffer.m);
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "matrixBuffer");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kElementsPerMatrix = 8;  // Each mat2 row gets padded into a vec4.
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kElementsPerMatrix * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[0u] = 1.0f;
        vAsFloat[1u] = 128.0f / 255.0f;
        vAsFloat[4u] = 64.0f / 255.0f;
        vAsFloat[5u] = 32.0f / 255.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 64, 128, 32), 5);
    }
    
    // Test a uniform block where an individual matrix field is set as column-major whereas the whole
    // block is set as row-major.
    TEST_P(UniformBufferTest, Std140UniformBlockWithPerMemberColumnMajorQualifier)
    {
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
    
            layout(std140, row_major) uniform matrixBuffer
            {
                // 2 columns, 3 rows.
                layout(column_major) mat2x3 m;
            } buffer;
    
            void main()
            {
                // Vector constructor accesses elements in column-major order.
                my_FragColor = vec4(buffer.m);
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "matrixBuffer");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kElementsPerMatrix = 8;  // Each mat2x3 column gets padded into a vec4.
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kElementsPerMatrix * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[0u] = 1.0f;
        vAsFloat[1u] = 192.0f / 255.0f;
        vAsFloat[2u] = 128.0f / 255.0f;
        vAsFloat[4u] = 96.0f / 255.0f;
        vAsFloat[5u] = 64.0f / 255.0f;
        vAsFloat[6u] = 32.0f / 255.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 192, 128, 96), 5);
    }
    
    // Test a uniform block where a struct field is set as row-major.
    TEST_P(UniformBufferTest, Std140UniformBlockWithRowMajorQualifierOnStruct)
    {
        // AMD OpenGL driver doesn't seem to apply the row-major qualifier right.
        // http://anglebug.com/2273
        ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL() && !IsOSX());
    
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
    
            struct S
            {
                mat2 m;
            };
    
            layout(std140) uniform matrixBuffer
            {
                layout(row_major) S s;
            } buffer;
    
            void main()
            {
                // Vector constructor accesses elements in column-major order.
                my_FragColor = vec4(buffer.s.m);
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "matrixBuffer");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kElementsPerMatrix = 8;  // Each mat2 row gets padded into a vec4.
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kElementsPerMatrix * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        vAsFloat[0u] = 1.0f;
        vAsFloat[1u] = 128.0f / 255.0f;
        vAsFloat[4u] = 64.0f / 255.0f;
        vAsFloat[5u] = 32.0f / 255.0f;
    
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 64, 128, 32), 5);
    }
    
    constexpr char kFragmentShader[] = R"(#version 300 es
    precision mediump float;
    
    layout (std140) uniform color_ubo
    {
      vec4 color;
    };
    
    out vec4 fragColor;
    void main()
    {
      fragColor = color;
    })";
    
    // Regression test for a dirty bit bug in ANGLE. See http://crbug.com/792966
    TEST_P(UniformBufferTest, SimpleBindingChange)
    {
        // http://anglebug.com/2287
        ANGLE_SKIP_TEST_IF(IsOSX() && IsNVIDIA() && IsDesktopOpenGL());
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFragmentShader);
    
        glBindAttribLocation(program, 0, essl3_shaders::PositionAttrib());
        glUseProgram(program);
        GLint uboIndex = glGetUniformBlockIndex(program, "color_ubo");
    
        std::array<GLfloat, 12> vertices{{-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0}};
        GLBuffer vertexBuf;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuf);
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(),
                     GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    
        std::array<GLshort, 12> indexData = {{0, 1, 2, 2, 1, 3, 0, 1, 2, 2, 1, 3}};
    
        GLBuffer indexBuf;
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.size() * sizeof(GLshort), indexData.data(),
                     GL_STATIC_DRAW);
    
        // Bind a first buffer with red.
        GLBuffer uboBuf1;
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboBuf1);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(GLColor32F), &kFloatRed, GL_STATIC_DRAW);
        glUniformBlockBinding(program, uboIndex, 0);
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    
        // Bind a second buffer with green, updating the buffer binding.
        GLBuffer uboBuf2;
        glBindBufferBase(GL_UNIFORM_BUFFER, 1, uboBuf2);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(GLColor32F), &kFloatGreen, GL_STATIC_DRAW);
        glUniformBlockBinding(program, uboIndex, 1);
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, reinterpret_cast<const GLvoid *>(12));
    
        // Verify we get the second buffer.
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Regression test for a dirty bit bug in ANGLE. Same as above but for the indexed bindings.
    TEST_P(UniformBufferTest, SimpleBufferChange)
    {
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFragmentShader);
    
        glBindAttribLocation(program, 0, essl3_shaders::PositionAttrib());
        glUseProgram(program);
        GLint uboIndex = glGetUniformBlockIndex(program, "color_ubo");
    
        std::array<GLfloat, 12> vertices{{-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0}};
        GLBuffer vertexBuf;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuf);
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(),
                     GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    
        std::array<GLshort, 12> indexData = {{0, 1, 2, 2, 1, 3, 0, 1, 2, 2, 1, 3}};
    
        GLBuffer indexBuf;
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.size() * sizeof(GLshort), indexData.data(),
                     GL_STATIC_DRAW);
    
        // Bind a first buffer with red.
        GLBuffer uboBuf1;
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboBuf1);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(GLColor32F), &kFloatRed, GL_STATIC_DRAW);
        glUniformBlockBinding(program, uboIndex, 0);
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    
        // Bind a second buffer to the same binding point (0). This should set to draw green.
        GLBuffer uboBuf2;
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboBuf2);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(GLColor32F), &kFloatGreen, GL_STATIC_DRAW);
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, reinterpret_cast<const GLvoid *>(12));
    
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Tests a bug in the D3D11 back-end where re-creating the buffer storage should trigger a state
    // update in the State Manager class.
    TEST_P(UniformBufferTest, DependentBufferChange)
    {
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFragmentShader);
    
        glBindAttribLocation(program, 0, essl3_shaders::PositionAttrib());
        glUseProgram(program);
        GLint uboIndex = glGetUniformBlockIndex(program, "color_ubo");
    
        std::array<GLfloat, 12> vertices{{-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0}};
        GLBuffer vertexBuf;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuf);
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(),
                     GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    
        std::array<GLshort, 6> indexData = {{0, 1, 2, 2, 1, 3}};
    
        GLBuffer indexBuf;
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.size() * sizeof(GLshort), indexData.data(),
                     GL_STATIC_DRAW);
    
        GLBuffer ubo;
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(GLColor32F), &kFloatRed, GL_STATIC_DRAW);
        glUniformBlockBinding(program, uboIndex, 0);
    
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        // Resize the buffer - triggers a re-allocation in the D3D11 back-end.
        std::vector<GLColor32F> bigData(128, kFloatGreen);
        glBufferData(GL_UNIFORM_BUFFER, sizeof(GLColor32F) * bigData.size(), bigData.data(),
                     GL_STATIC_DRAW);
    
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Recreate WebGL conformance test conformance2/uniforms/large-uniform-buffers.html to test
    // regression in http://anglebug.com/3388
    TEST_P(UniformBufferTest, SizeOverMaxBlockSize)
    {
        // Test crashes on Windows AMD OpenGL
        ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL());
        // http://anglebug.com/5382
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL());
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFragmentShader);
    
        glBindAttribLocation(program, 0, essl3_shaders::PositionAttrib());
        glUseProgram(program);
        GLint uboIndex = glGetUniformBlockIndex(program, "color_ubo");
    
        std::array<GLfloat, 12> vertices{{-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0}};
        GLBuffer vertexBuf;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuf);
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(),
                     GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    
        std::array<GLshort, 6> indexData = {{0, 1, 2, 2, 1, 3}};
    
        GLBuffer indexBuf;
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.size() * sizeof(GLshort), indexData.data(),
                     GL_STATIC_DRAW);
    
        GLint uboDataSize = 0;
        glGetActiveUniformBlockiv(program, uboIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &uboDataSize);
        EXPECT_NE(uboDataSize, 0);  // uniform block data size invalid
    
        GLint64 maxUniformBlockSize;
        glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
    
        GLBuffer uboBuf;
        std::vector<GLfloat> uboData;
        uboData.resize(maxUniformBlockSize * 2);  // underlying data is twice the max block size
    
        GLint offs0 = 0;
    
        // Red
        uboData[offs0 + 0] = 1;
        uboData[offs0 + 1] = 0;
        uboData[offs0 + 2] = 0;
        uboData[offs0 + 3] = 1;
    
        GLint offs1     = maxUniformBlockSize;
        GLint alignment = 0;
        glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment);
        EXPECT_EQ(offs1 % alignment, 0);
    
        // Green
        uboData[offs1 + 0] = 0;
        uboData[offs1 + 1] = 1;
        uboData[offs1 + 2] = 0;
        uboData[offs1 + 3] = 1;
    
        glUniformBlockBinding(program, uboIndex, 0);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboBuf);
        glBufferData(GL_UNIFORM_BUFFER, uboData.size() * sizeof(GLfloat), uboData.data(),
                     GL_STATIC_DRAW);
        ASSERT_GL_NO_ERROR();  // No errors from setup
    
        // Draw lower triangle - should be red
        glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboBuf, offs0 * sizeof(float), 4 * sizeof(float));
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
        ASSERT_GL_NO_ERROR();  // No errors from draw
    
        // Draw upper triangle - should be green
        glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboBuf, offs1 * sizeof(float), 4 * sizeof(float));
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT,
                       reinterpret_cast<void *>(3 * sizeof(GLshort)));
        ASSERT_GL_NO_ERROR();  // No errors from draw
    
        GLint width  = getWindowWidth();
        GLint height = getWindowHeight();
        // Lower left should be red
        EXPECT_PIXEL_COLOR_EQ(width / 2 - 5, height / 2 - 5, GLColor::red);
        // Top right should be green
        EXPECT_PIXEL_COLOR_EQ(width / 2 + 5, height / 2 + 5, GLColor::green);
    }
    
    // Compile uniform buffer with large array member.
    TEST_P(UniformBufferTest, LargeArrayOfStructs)
    {
        constexpr char kVertexShader[] = R"(
            struct InstancingData
            {
                mat4 transformation;
            };
    
            layout(std140) uniform InstanceBlock
            {
                InstancingData instances[MAX_INSTANCE_COUNT];
            };
    
            void main()
            {
                gl_Position = vec4(1.0) * instances[gl_InstanceID].transformation;
            })";
    
        constexpr char kFragmentShader[] = R"(#version 300 es
            precision mediump float;
            out vec4 outFragColor;
            void main()
            {
                outFragColor = vec4(0.0);
            })";
    
        int maxUniformBlockSize;
        glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
    
        std::string vs = "#version 300 es\n#define MAX_INSTANCE_COUNT " +
                         std::to_string(std::min(800, maxUniformBlockSize / 64)) + kVertexShader;
    
        ANGLE_GL_PROGRAM(program, vs.c_str(), kFragmentShader);
        // Add a draw call for the sake of the Vulkan backend that currently really builds shaders at
        // draw time.
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
    }
    
    // Test a uniform block where an array of row-major matrices is dynamically indexed.
    TEST_P(UniformBufferTest, Std140UniformBlockWithDynamicallyIndexedRowMajorArray)
    {
        // http://anglebug.com/3837 , http://anglebug.com/2273
        ANGLE_SKIP_TEST_IF((IsLinux() && IsIntel() && IsOpenGL()) || IsOSX());
    
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
    
            uniform int u_zero;
    
            layout(std140, row_major) uniform matrixBuffer {
                mat4 u_mats[1];
            };
    
            void main() {
                float f = u_mats[u_zero + 0][2][1];
                my_FragColor = vec4(1.0 - f, f, 0.0, 1.0);
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "matrixBuffer");
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        const GLsizei kElementsPerMatrix = 16;  // Each mat2 row gets padded into a vec4.
        const GLsizei kBytesPerElement   = 4;
        const GLsizei kDataSize          = kElementsPerMatrix * kBytesPerElement;
        std::vector<GLubyte> v(kDataSize, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
        // Write out this initializer to make it clearer what the matrix contains.
        float matrixData[kElementsPerMatrix] = {
            // clang-format off
            0.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 0.0f,
            // clang-format on
        };
        for (int ii = 0; ii < kElementsPerMatrix; ++ii)
        {
            vAsFloat[ii] = matrixData[ii];
        }
        glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
        GLint indexLoc = glGetUniformLocation(program, "u_zero");
        glUseProgram(program);
        glUniform1i(indexLoc, 0);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 255, 0, 255), 5);
    }
    
    // Test with many uniform buffers work as expected.
    TEST_P(UniformBufferTest, ManyBlocks)
    {
        // http://anglebug.com/5039
        ANGLE_SKIP_TEST_IF(IsD3D11());
    
        // http://anglebug.com/5283
        ANGLE_SKIP_TEST_IF(IsMetal() && IsIntel());
    
        constexpr char kFS[] =
            R"(#version 300 es
    
            precision highp float;
            out vec4 my_FragColor;
    
            layout(std140) uniform uboBlock { vec4 color; } blocks[12];
    
            void main()
            {
                vec4 color = vec4(0, 0, 0, 1);
                color += blocks[0].color;
                color += blocks[1].color;
                color += blocks[2].color;
                color += blocks[3].color;
                color += blocks[4].color;
                color += blocks[5].color;
                color += blocks[6].color;
                color += blocks[7].color;
                color += blocks[8].color;
                color += blocks[9].color;
                color += blocks[10].color;
                color += blocks[11].color;
                my_FragColor = vec4(color.rgb, 1.0);
            })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        GLBuffer buffers[12];
        GLint bufferIndex[12];
        bufferIndex[0]  = glGetUniformBlockIndex(program, "uboBlock[0]");
        bufferIndex[1]  = glGetUniformBlockIndex(program, "uboBlock[1]");
        bufferIndex[2]  = glGetUniformBlockIndex(program, "uboBlock[2]");
        bufferIndex[3]  = glGetUniformBlockIndex(program, "uboBlock[3]");
        bufferIndex[4]  = glGetUniformBlockIndex(program, "uboBlock[4]");
        bufferIndex[5]  = glGetUniformBlockIndex(program, "uboBlock[5]");
        bufferIndex[6]  = glGetUniformBlockIndex(program, "uboBlock[6]");
        bufferIndex[7]  = glGetUniformBlockIndex(program, "uboBlock[7]");
        bufferIndex[8]  = glGetUniformBlockIndex(program, "uboBlock[8]");
        bufferIndex[9]  = glGetUniformBlockIndex(program, "uboBlock[9]");
        bufferIndex[10] = glGetUniformBlockIndex(program, "uboBlock[10]");
        bufferIndex[11] = glGetUniformBlockIndex(program, "uboBlock[11]");
    
        std::vector<GLubyte> v(16, 0);
        float *vAsFloat = reinterpret_cast<float *>(v.data());
    
        for (int i = 0; i < 12; ++i)
        {
            glBindBuffer(GL_UNIFORM_BUFFER, buffers[i]);
            vAsFloat[0] = (i + 1) / 255.0f;
            vAsFloat[1] = (i + 1) / 255.0f;
            vAsFloat[2] = (i + 1) / 255.0f;
            vAsFloat[3] = .0f;
    
            glBufferData(GL_UNIFORM_BUFFER, v.size(), v.data(), GL_STATIC_DRAW);
    
            glBindBufferBase(GL_UNIFORM_BUFFER, i, buffers[i]);
            glUniformBlockBinding(program, bufferIndex[i], i);
        }
    
        glViewport(0, 0, getWindowWidth() / 2, getWindowHeight());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
    
        // Modify buffer[1]
        glBindBuffer(GL_UNIFORM_BUFFER, buffers[1]);
    
        vAsFloat[0] = 2 / 255.0f;
        vAsFloat[1] = 22 / 255.0f;  // green channel increased by 20
        vAsFloat[2] = 2 / 255.0f;
        vAsFloat[3] = .0f;
    
        glBufferData(GL_UNIFORM_BUFFER, v.size(), v.data(), GL_STATIC_DRAW);
    
        glViewport(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
    
        // First draw
        EXPECT_PIXEL_NEAR(0, 0, 78, 78, 78, 255, 2);
        // Second draw: green channel increased by 20
        EXPECT_PIXEL_NEAR(getWindowWidth() / 2, 0, 78, 98, 78, 255, 2);
    }
    
    // These suite cases test the uniform blocks with a large array member. Unlike other uniform
    // blocks that will be translated to cbuffer type on D3D backend, we will tranlate these
    // uniform blocks to StructuredBuffer for slow fxc compile performance issue with dynamic
    // uniform indexing, angleproject/3682.
    class UniformBlockWithOneLargeArrayMemberTest : public ANGLETest
    {
      protected:
        UniformBlockWithOneLargeArrayMemberTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        void testSetUp() override
        {
            glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &mMaxUniformBlockSize);
            // Ensure that shader uniform block does not exceed MAX_UNIFORM_BLOCK_SIZE limit.
            if (mMaxUniformBlockSize >= 16384 && mMaxUniformBlockSize < 32768)
            {
                mArraySize1 = 128;
                mArraySize2 = 8;
                mDivisor1   = 128;
                mDivisor2   = 32;
                mDivisor3   = 16;
            }
            else if (mMaxUniformBlockSize >= 32768 && mMaxUniformBlockSize < 65536)
            {
                mArraySize1 = 256;
                mArraySize2 = 16;
                mDivisor1   = 64;
                mDivisor2   = 16;
                mDivisor3   = 8;
            }
            else
            {
                mArraySize1 = 512;
                mArraySize2 = 32;
                mDivisor1   = 32;
                mDivisor2   = 8;
                mDivisor3   = 4;
            }
    
            glGenBuffers(1, &mUniformBuffer);
            ASSERT_GL_NO_ERROR();
        }
    
        void testTearDown() override { glDeleteBuffers(1, &mUniformBuffer); }
    
        void generateArraySizeAndDivisorsDeclaration(std::ostringstream &out,
                                                     bool hasArraySize2,
                                                     bool hasDivisor2,
                                                     bool hasDivisor3)
        {
            if (hasArraySize2)
            {
                out << "const uint arraySize1 = " << mArraySize1 << "u;\n";
                out << "const uint arraySize2 = " << mArraySize2 << "u;\n";
            }
            else
            {
                out << "const uint arraySize = " << mArraySize1 << "u;\n";
            }
    
            if (hasDivisor2)
            {
                out << "const uint divisor1 = " << mDivisor1 << "u;\n";
                out << "const uint divisor2 = " << mDivisor2 << "u;\n";
            }
            else
            {
                out << "const uint divisor = " << mDivisor1 << "u;\n";
            }
            if (hasDivisor3)
            {
                out << "const uint divisor3 = " << mDivisor3 << "u;\n";
            }
        }
        GLuint getArraySize() { return mArraySize1; }
        GLuint getArraySize2() { return mArraySize2; }
    
        void setArrayValues(std::vector<GLfloat> &floatData,
                            GLuint beginIndex,
                            GLuint endIndex,
                            GLuint stride,
                            GLuint firstElementOffset,
                            GLuint firstEleVecCount,
                            GLuint firstEleVecComponents,
                            float x1,
                            float y1,
                            float z1,
                            float w1,
                            GLuint secondElementOffset    = 0,
                            GLuint secondEleVecCount      = 0,
                            GLuint secondEleVecComponents = 0,
                            float x2                      = 0.0f,
                            float y2                      = 0.0f,
                            float z2                      = 0.0f,
                            float w2                      = 0.0f)
        {
            for (GLuint i = beginIndex; i < endIndex; i++)
            {
                for (GLuint j = 0; j < firstEleVecCount; j++)
                {
                    if (firstEleVecComponents > 3)
                    {
                        floatData[i * stride + firstElementOffset + 4 * j + 3] = w1;
                    }
                    if (firstEleVecComponents > 2)
                    {
                        floatData[i * stride + firstElementOffset + 4 * j + 2] = z1;
                    }
                    if (firstEleVecComponents > 1)
                    {
                        floatData[i * stride + firstElementOffset + 4 * j + 1] = y1;
                    }
                    floatData[i * stride + firstElementOffset + 4 * j] = x1;
                }
    
                for (GLuint k = 0; k < secondEleVecCount; k++)
                {
                    if (secondEleVecComponents > 3)
                    {
                        floatData[i * stride + secondElementOffset + 4 * k + 3] = w2;
                    }
                    if (secondEleVecComponents > 2)
                    {
                        floatData[i * stride + secondElementOffset + 4 * k + 2] = z2;
                    }
                    if (secondEleVecComponents > 1)
                    {
                        floatData[i * stride + secondElementOffset + 4 * k + 1] = y2;
                    }
                    floatData[i * stride + secondElementOffset + 4 * k] = x2;
                }
            }
        }
    
        void checkResults(const GLColor &firstQuarter,
                          const GLColor &secondQuarter,
                          const GLColor &thirdQuarter,
                          const GLColor &fourthQuarter)
        {
            for (GLuint i = 0; i < kPositionCount; i++)
            {
                if (positionToTest[i][1] >= 0 && positionToTest[i][1] < 32)
                {
                    EXPECT_PIXEL_COLOR_EQ(positionToTest[i][0], positionToTest[i][1], firstQuarter);
                }
                else if (positionToTest[i][1] >= 32 && positionToTest[i][1] < 64)
                {
                    EXPECT_PIXEL_COLOR_EQ(positionToTest[i][0], positionToTest[i][1], secondQuarter);
                }
                else if (positionToTest[i][1] >= 64 && positionToTest[i][1] < 96)
                {
                    EXPECT_PIXEL_COLOR_EQ(positionToTest[i][0], positionToTest[i][1], thirdQuarter);
                }
                else
                {
                    EXPECT_PIXEL_COLOR_EQ(positionToTest[i][0], positionToTest[i][1], fourthQuarter);
                }
            }
        }
    
        GLuint mUniformBuffer;
        GLint64 mMaxUniformBlockSize;
        GLuint mArraySize1;
        GLuint mArraySize2;
        GLuint mDivisor1;
        GLuint mDivisor2;
        GLuint mDivisor3;
    
        static constexpr GLuint kVectorPerMat                           = 4;
        static constexpr GLuint kFloatPerVector                         = 4;
        static constexpr GLuint kPositionCount                          = 12;
        static constexpr unsigned int positionToTest[kPositionCount][2] = {
            {0, 0},   {75, 0},  {98, 13}, {31, 31}, {0, 32},   {65, 33},
            {23, 54}, {63, 63}, {0, 64},  {43, 86}, {53, 100}, {127, 127}};
    };
    
    // Test uniform block whose member is structure type, which contains a mat4 member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsStruct)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color;};\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = s[index_x].color[index_y];\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kVectorPerMat * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 1.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, 16, 0, 4, 4, 1.0f, 0.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::red, GLColor::green, GLColor::green);
    }
    
    // Test instanced uniform block whose member is structure type, which contains a mat4 member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsStructAndInstanced)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color;};\n"
            "layout(std140) uniform buffer { S s[arraySize]; } instance;\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = instance.s[index_x].color[index_y];\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kVectorPerMat * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 1.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, 16, 0, 4, 4, 1.0f, 0.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::red, GLColor::green, GLColor::green);
    }
    
    // Test instanced uniform block array whose member is structure type, which contains a mat4 member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsStructAndInstancedArray)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color;};\n"
            "layout(std140) uniform buffer { S s[arraySize]; } instance[2];\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = instance[0].s[index_x].color[index_y] + "
            "instance[1].s[index_x].color[index_y];\n"
            "}\n";
    
        GLint blockSize0, blockSize1;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex0 = glGetUniformBlockIndex(program, "buffer[0]");
        GLint uniformBufferIndex1 = glGetUniformBlockIndex(program, "buffer[1]");
        glGetActiveUniformBlockiv(program, uniformBufferIndex0, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize0);
        glGetActiveUniformBlockiv(program, uniformBufferIndex1, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize1);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize0, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex0, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kVectorPerMat * kFloatPerVector;
        std::vector<GLfloat> floatData0(floatCount, 0.0f);
        std::vector<GLfloat> floatData1(floatCount, 0.0f);
    
        setArrayValues(floatData0, 0, arraySize, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData0.data());
    
        GLBuffer uniformBuffer1;
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer1);
        glBufferData(GL_UNIFORM_BUFFER, blockSize1, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 1, uniformBuffer1);
        glUniformBlockBinding(program, uniformBufferIndex0, 1);
    
        setArrayValues(floatData1, 0, arraySize, 16, 0, 4, 4, 0.0f, 1.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData1.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::cyan, GLColor::cyan, GLColor::cyan, GLColor::cyan);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        setArrayValues(floatData0, 0, arraySize, 16, 0, 4, 4, 1.0f, 0.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData0.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::yellow, GLColor::yellow, GLColor::yellow, GLColor::yellow);
    
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer1);
        setArrayValues(floatData1, arraySize / 4, arraySize / 2, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData1.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::yellow, GLColor::magenta, GLColor::yellow, GLColor::yellow);
    }
    
    // Test uniform block whose member is structure type, which contains a mat4 member and a float
    // member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMixStructMat4AndFloat)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color; float factor; };\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = s[index_x].factor * s[index_x].color[index_y];\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        // The member s is an array of S structures, each element of s should be rounded up
        // to the base alignment of a vec4 according to std140 storage layout rules.
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * (kVectorPerMat * kFloatPerVector + kFloatPerVector);
        std::vector<GLfloat> floatData(floatCount, 0.0f);
        const size_t strideofFloatCount = kVectorPerMat * kFloatPerVector + kFloatPerVector;
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 4, 4, 0.0f, 0.0f, 0.5f, 0.5f, 16,
                       1, 1, 2.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 4, 4, 0.0f, 0.5f, 0.0f, 0.5f, 16,
                       1, 1, 2.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, strideofFloatCount, 0, 4, 4, 0.5f, 0.0f,
                       0.0f, 0.5f, 16, 1, 1, 2.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::red, GLColor::green, GLColor::green);
    }
    
    // Test uniform block whose member is structure type, which contains a vec2 member and a vec3
    // member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMixStructVec2AndVec3)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { vec2 color1; vec3 color2; };\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = vec4(s[index].color1, s[index].color2.xy);\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        // The base alignment of "color2" is vec4.
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * 2 * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
        const size_t strideofFloatCount = 2 * kFloatPerVector;
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 2, 1.0f, 1.0f, 0.0f, 0.0f, 4,
                       1, 3, 1.0f, 1.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::white, GLColor::white, GLColor::white, GLColor::white);
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 2, 1.0f, 0.0f, 0.0f, 0.0f, 4,
                       1, 3, 0.0f, 1.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::red, GLColor::red, GLColor::red);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, strideofFloatCount, 0, 1, 2, 0.0f, 0.0f,
                       0.0f, 0.0f, 4, 1, 3, 1.0f, 1.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::blue, GLColor::red, GLColor::red);
    }
    
    // Test uniform block whose member is structure type, which contains a float member and a vec3
    // member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMixStructFloatAndVec3)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { float color1; vec3 color2; };\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = vec4(s[index].color1, s[index].color2);\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        // The base alignment of "color2" is vec4.
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * 2 * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
        const size_t strideofFloatCount = 2 * kFloatPerVector;
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 1, 1.0f, 0.0f, 0.0f, 0.0f, 4,
                       1, 3, 1.0f, 1.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::white, GLColor::white, GLColor::white, GLColor::white);
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 1, 1.0f, 0.0f, 0.0f, 0.0f, 4,
                       1, 3, 0.0f, 0.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::red, GLColor::red, GLColor::red);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, strideofFloatCount, 0, 1, 1, 0.0f, 0.0f,
                       0.0f, 0.0f, 4, 1, 3, 0.0f, 1.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::blue, GLColor::red, GLColor::red);
    }
    
    // Test uniform block whose member is structure type, which contains a vec3 member and a float
    // member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMixStructVec3AndFloat)
    {
        ANGLE_SKIP_TEST_IF(IsOSX());
    
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { vec3 color1; float color2; };\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = vec4(s[index].color2, s[index].color1);\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
        const size_t strideofFloatCount = kFloatPerVector;
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 3, 1.0f, 1.0f, 1.0f, 0.0f, 3,
                       1, 1, 1.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::white, GLColor::white, GLColor::white, GLColor::white);
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 3, 0.0f, 0.0f, 1.0f, 0.0f, 3,
                       1, 1, 1.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::red, GLColor::red, GLColor::red);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, strideofFloatCount, 0, 1, 3, 0.0f, 1.0f,
                       1.0f, 0.0f, 3, 1, 1, 0.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::blue, GLColor::red, GLColor::red);
    }
    
    // Test two uniform blocks with large structure array member are in the same program, and they
    // share the same uniform buffer.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, TwoUniformBlocksInSameProgram)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, true, true, true);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color;};\n"
            "layout(std140) uniform buffer1 { S s1[arraySize1]; };\n"
            "layout(std140) uniform buffer2 { S s2[arraySize2]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x1 = index / divisor1;\n"
            "    uint index_y1 = (index % divisor1) / divisor2;\n"
            "    uint index_x2 = coord.x / divisor3;\n"
            "    uint index_y2 = coord.x % 4u;\n"
            "    my_FragColor = s1[index_x1].color[index_y1] + s2[index_x2].color[index_y2];\n"
            "}\n";
    
        GLint blockSize1, blockSize2;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex1 = glGetUniformBlockIndex(program, "buffer1");
        GLint uniformBufferIndex2 = glGetUniformBlockIndex(program, "buffer2");
        glGetActiveUniformBlockiv(program, uniformBufferIndex1, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize1);
        glGetActiveUniformBlockiv(program, uniformBufferIndex2, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize2);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize1 + blockSize2, nullptr, GL_STATIC_DRAW);
    
        glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, 0, blockSize2);
        glUniformBlockBinding(program, uniformBufferIndex2, 0);
        glBindBufferRange(GL_UNIFORM_BUFFER, 1, mUniformBuffer, blockSize2, blockSize1);
        glUniformBlockBinding(program, uniformBufferIndex1, 1);
    
        const GLuint arraySize1  = getArraySize();
        const GLuint arraySize2  = getArraySize2();
        const GLuint floatCount1 = arraySize1 * kVectorPerMat * kFloatPerVector;
        const GLuint floatCount2 = arraySize2 * kVectorPerMat * kFloatPerVector;
        const GLuint floatCount  = floatCount1 + floatCount2;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, arraySize2, arraySize1 + arraySize2, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f,
                       1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount2 * sizeof(GLfloat), &floatData[0]);
        glBufferSubData(GL_UNIFORM_BUFFER, blockSize2, floatCount1 * sizeof(GLfloat),
                        &floatData[floatCount2]);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize2, 16, 0, 4, 4, 0.0f, 1.0f, 0.0f, 0.0f);
        setArrayValues(floatData, arraySize1 / 4, arraySize2 + arraySize1 / 2, 16, 0, 4, 4, 1.0f, 0.0f,
                       0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount2 * sizeof(GLfloat), &floatData[0]);
        glBufferSubData(GL_UNIFORM_BUFFER, blockSize2 + floatCount1 * sizeof(GLfloat) / 4,
                        floatCount1 * sizeof(GLfloat) / 4, &floatData[floatCount2 + floatCount1 / 4]);
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::cyan, GLColor::yellow, GLColor::cyan, GLColor::cyan);
    }
    
    // Test a uniform block with large struct array member and a uniform block with small
    // struct array member in different programs, but they share the same uniform buffer.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, TwoUniformBlocksInDiffProgram)
    {
        std::ostringstream stream1;
        std::ostringstream stream2;
        generateArraySizeAndDivisorsDeclaration(stream1, false, true, false);
        generateArraySizeAndDivisorsDeclaration(stream2, false, false, false);
    
        const std::string &kFS1 =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream1.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color;};\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = s[index_x].color[index_y];\n"
            "}\n";
    
        const std::string &kFS2 =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream2.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color;};\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index_x = coord.x / divisor;\n"
            "    uint index_y = coord.x % 4u;\n"
            "    my_FragColor = s[index_x].color[index_y];\n"
            "}\n";
    
        GLint blockSize1, blockSize2;
        ANGLE_GL_PROGRAM(program1, essl3_shaders::vs::Simple(), kFS1.c_str());
        ANGLE_GL_PROGRAM(program2, essl3_shaders::vs::Simple(), kFS2.c_str());
        GLint uniformBufferIndex1 = glGetUniformBlockIndex(program1, "buffer");
        GLint uniformBufferIndex2 = glGetUniformBlockIndex(program2, "buffer");
        glGetActiveUniformBlockiv(program1, uniformBufferIndex1, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize1);
        glGetActiveUniformBlockiv(program2, uniformBufferIndex2, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize2);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, std::max(blockSize1, blockSize2), nullptr, GL_STATIC_DRAW);
    
        glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, 0, blockSize2);
        glUniformBlockBinding(program2, uniformBufferIndex2, 0);
        glBindBufferRange(GL_UNIFORM_BUFFER, 1, mUniformBuffer, 0, blockSize1);
        glUniformBlockBinding(program1, uniformBufferIndex1, 1);
    
        const GLuint arraySize1  = getArraySize();
        const GLuint arraySize2  = getArraySize2();
        const GLuint floatCount1 = arraySize1 * kVectorPerMat * kFloatPerVector;
        const GLuint floatCount2 = arraySize2 * kVectorPerMat * kFloatPerVector;
        const GLuint floatCount  = floatCount1;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize1, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), &floatData[0]);
        drawQuad(program1.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize2, 16, 0, 4, 4, 0.0f, 1.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount2 * sizeof(GLfloat), &floatData[0]);
        drawQuad(program2.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize2, arraySize2 + arraySize1 / 2, 16, 0, 4, 4, 0.0f, 1.0f,
                       0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, floatCount2 * sizeof(GLfloat),
                        (floatCount1 / 2 - floatCount2) * sizeof(GLfloat), &floatData[0]);
        drawQuad(program1.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::blue, GLColor::blue);
    }
    
    // Test two uniform blocks share the same uniform buffer. On D3D backend, a uniform
    // block with a large array member will be translated to StructuredBuffer, and the
    // other uniform block will be translated to cbuffer, this case verifies that update
    // buffer data correctly.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, SharedSameBufferWithOtherOne)
    {
        ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX() && IsOpenGL());
    
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { mat4 color;};\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "layout(std140) uniform buffer1 { vec4 factor; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = s[index_x].color[index_y] + factor;\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
        GLint uniformBufferIndex1 = glGetUniformBlockIndex(program, "buffer1");
        GLint alignment;
        glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment);
        while (alignment >= 0 && alignment < 16)
        {
            alignment += alignment;
        }
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, alignment + blockSize, nullptr, GL_STATIC_DRAW);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kVectorPerMat * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
        std::vector<GLfloat> floatData1(4, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 0.0f, 0.5f, 0.5f);
        setArrayValues(floatData1, 0, 1, 4, 0, 1, 4, 1.0f, 0.0f, 0.5f, 0.5f);
        glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, 0, 4 * sizeof(float));
        glUniformBlockBinding(program, uniformBufferIndex1, 0);
        glBindBufferRange(GL_UNIFORM_BUFFER, 1, mUniformBuffer, alignment, floatCount * sizeof(float));
        glUniformBlockBinding(program, uniformBufferIndex, 1);
    
        glBufferSubData(GL_UNIFORM_BUFFER, alignment, floatCount * sizeof(GLfloat), floatData.data());
        glBufferSubData(GL_UNIFORM_BUFFER, 0, 4 * sizeof(GLfloat), floatData1.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::magenta, GLColor::magenta, GLColor::magenta, GLColor::magenta);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 0.5f, 0.0f, 0.5f);
        setArrayValues(floatData1, 0, 1, 4, 0, 1, 4, 0.0f, 0.5f, 0.0f, 0.5f);
        glBufferSubData(GL_UNIFORM_BUFFER, alignment, floatCount * sizeof(GLfloat), floatData.data());
        glBufferSubData(GL_UNIFORM_BUFFER, 0, 4 * sizeof(GLfloat), floatData1.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    }
    
    // Test indexing accesses uniform block with a large matrix array member correctly.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMatrixAndIndexAccess)
    {
        const char *kFS = R"(#version 300 es
    precision mediump float;
    
    uniform uint index;
    
    struct S { uvec4 idx; };
    
    layout(std140) uniform idxbuf { S idxArray[2]; };
    
    layout(std140) uniform buffer1 { mat4 s1[128]; };
    layout(std140) uniform buffer2 { mat4 s2[128]; } buf2[2];
    
    out vec4 fragColor;
    void main()
    {
      fragColor = s1[1][0] + s1[index][1] + s1[idxArray[0].idx.x][idxArray[1].idx.z]
      + buf2[0].s2[1][0] + buf2[1].s2[index][1] + buf2[0].s2[idxArray[0].idx.y][idxArray[1].idx.z]
      + vec4(buf2[1].s2[index][1][index], s1[1][0][2], s1[idxArray[0].idx.x][idxArray[1].idx.z][2],
      buf2[0].s2[idxArray[0].idx.y][idxArray[1].idx.z][3]);
    })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        ASSERT_GL_NO_ERROR();
    }
    
    // Test uniform block whose member is matrix type.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMatrix)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform buffer { mat4 s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = s[index_x][index_y];\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kVectorPerMat * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 4, 4, 0.0f, 1.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, 16, 0, 4, 4, 1.0f, 0.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::red, GLColor::green, GLColor::green);
    }
    
    // Test instanced uniform block whose member is matrix type.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMatrixAndInstanced)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform buffer { mat4 s[arraySize]; } instance[2];\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = instance[0].s[index_x][index_y] + "
            "instance[1].s[index_x][index_y];\n"
            "}\n";
    
        GLint blockSize0, blockSize1;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex0 = glGetUniformBlockIndex(program, "buffer[0]");
        GLint uniformBufferIndex1 = glGetUniformBlockIndex(program, "buffer[1]");
        glGetActiveUniformBlockiv(program, uniformBufferIndex0, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize0);
        glGetActiveUniformBlockiv(program, uniformBufferIndex1, GL_UNIFORM_BLOCK_DATA_SIZE,
                                  &blockSize1);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize0, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex0, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kVectorPerMat * kFloatPerVector;
        std::vector<GLfloat> floatData0(floatCount, 0.0f);
        std::vector<GLfloat> floatData1(floatCount, 0.0f);
    
        setArrayValues(floatData0, 0, arraySize, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData0.data());
    
        GLBuffer uniformBuffer1;
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer1);
        glBufferData(GL_UNIFORM_BUFFER, blockSize1, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 1, uniformBuffer1);
        glUniformBlockBinding(program, uniformBufferIndex0, 1);
    
        setArrayValues(floatData1, 0, arraySize, 16, 0, 4, 4, 0.0f, 1.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData1.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::cyan, GLColor::cyan, GLColor::cyan, GLColor::cyan);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        setArrayValues(floatData0, 0, arraySize, 16, 0, 4, 4, 1.0f, 0.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData0.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::yellow, GLColor::yellow, GLColor::yellow, GLColor::yellow);
    
        glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer1);
        setArrayValues(floatData1, arraySize / 4, arraySize / 2, 16, 0, 4, 4, 0.0f, 0.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData1.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::yellow, GLColor::magenta, GLColor::yellow, GLColor::yellow);
    }
    
    // Test uniform block with row major qualifier whose member is matrix type.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMatrixAndRowMajorQualifier)
    {
        // http://anglebug.com/3837 , http://anglebug.com/2273
        ANGLE_SKIP_TEST_IF((IsOSX() && IsOpenGL()) || IsAndroid() || (IsAMD() && IsOpenGL()) ||
                           (IsLinux() && IsIntel() && IsOpenGL()));
    
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "layout(std140, row_major) uniform buffer { mat4 s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = s[index_x][index_y];\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kVectorPerMat * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 16, 0, 2, 4, 0.0f, 0.0f, 0.0f, 0.0f, 8, 2, 4, 1.0f,
                       1.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize, 16, 4, 1, 4, 1.0f, 1.0f, 1.0f, 1.0f, 8, 1, 4, 0.0f,
                       0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, 16, 0, 1, 4, 1.0f, 1.0f, 1.0f, 1.0f, 4,
                       1, 4, 0.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::red, GLColor::green, GLColor::green);
    }
    
    // Test uniform block whose member is vec4 type.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsVec4)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform buffer { vec4 s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = s[index];\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 4, 0, 1, 4, 1.0f, 0.0f, 1.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::magenta, GLColor::magenta, GLColor::magenta, GLColor::magenta);
    
        setArrayValues(floatData, 0, arraySize, 4, 0, 1, 4, 1.0f, 1.0f, 0.0f, 1.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::yellow, GLColor::yellow, GLColor::yellow, GLColor::yellow);
    }
    
    // Test uniform block whose member is vec3 type.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsVec3)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform buffer { vec3 s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = vec4(s[index], 1.0);\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 4, 0, 1, 3, 0.0f, 0.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize, 4, 0, 1, 3, 0.0f, 1.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, 4, 0, 1, 3, 1.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::red, GLColor::green, GLColor::green);
    }
    
    // Test uniform block whose member is vec2 type.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsVec2)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform buffer { vec2 s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = vec4(s[index], s[index].x, 1.0);\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 4, 0, 1, 2, 1.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::magenta, GLColor::magenta, GLColor::magenta, GLColor::magenta);
    
        setArrayValues(floatData, 0, arraySize, 4, 0, 1, 2, 0.0f, 1.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, 4, 0, 1, 2, 1.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::magenta, GLColor::green, GLColor::green);
    }
    
    // Test uniform block whose member is float type.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsFloat)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "layout(std140) uniform buffer { float s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = vec4(s[index], 0.0, 0.0, 1.0);\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        // The base alignment and array stride are rounded up to the base alignment of a vec4.
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
    
        setArrayValues(floatData, 0, arraySize, 4, 0, 1, 1, 1.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::red, GLColor::red, GLColor::red);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, 4, 0, 1, 1, 0.0f, 0.0f, 0.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, floatCount * sizeof(GLfloat), floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::black, GLColor::red, GLColor::red);
    }
    
    // Test uniform block whose member is structure type, which contains a float member and a mat4
    // member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMixStructFloatAndMat4)
    {
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, true, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { float factor; mat4 color; };\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = coord.x +  coord.y * 128u;\n"
            "    uint index_x = index / divisor1;\n"
            "    uint index_y = (index % divisor1) / divisor2;\n"
            "    my_FragColor = s[index_x].factor * s[index_x].color[index_y];\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        // The member s is an array of S structures, each element of s should be rounded up
        // to the base alignment of a vec4 according to std140 storage layout rules.
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * (kVectorPerMat * kFloatPerVector + kFloatPerVector);
        std::vector<GLfloat> floatData(floatCount, 0.0f);
        const size_t strideofFloatCount = kVectorPerMat * kFloatPerVector + kFloatPerVector;
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 1, 2.0f, 0.0f, 0.0f, 0.0f, 4,
                       4, 4, 0.0f, 0.0f, 0.5f, 0.5f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::blue, GLColor::blue, GLColor::blue, GLColor::blue);
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 1, 2.0f, 0.0f, 0.0f, 0.0f, 4,
                       4, 4, 0.0f, 0.5f, 0.0f, 0.5f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::green, GLColor::green, GLColor::green);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, strideofFloatCount, 0, 1, 1, 2.0f, 0.0f,
                       0.0f, 0.0f, 4, 4, 4, 0.5f, 0.0f, 0.0f, 0.5f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::green, GLColor::red, GLColor::green, GLColor::green);
    }
    
    // Test uniform block whose member is structure type, which contains a float member and a vec4
    // member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberTypeIsMixStructFloatAndVec4)
    {
        ANGLE_SKIP_TEST_IF(IsOSX());
    
        std::ostringstream stream;
        generateArraySizeAndDivisorsDeclaration(stream, false, false, false);
        const std::string &kFS =
            "#version 300 es\n"
            "precision highp float;\n" +
            stream.str() +
            "out vec4 my_FragColor;\n"
            "struct S { float color1; vec4 color2; };\n"
            "layout(std140) uniform buffer { S s[arraySize]; };\n"
            "void main()\n"
            "{\n"
            "    uvec2 coord = uvec2(floor(gl_FragCoord.xy));\n"
            "    uint index = (coord.x +  coord.y * 128u) / divisor;\n"
            "    my_FragColor = vec4(s[index].color1, s[index].color2.xyz);\n"
            "}\n";
    
        GLint blockSize;
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS.c_str());
        GLint uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
        glGetActiveUniformBlockiv(program, uniformBufferIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
    
        glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
        glBufferData(GL_UNIFORM_BUFFER, blockSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
        glUniformBlockBinding(program, uniformBufferIndex, 0);
    
        const GLuint arraySize  = getArraySize();
        const GLuint floatCount = arraySize * 2 * kFloatPerVector;
        std::vector<GLfloat> floatData(floatCount, 0.0f);
        const size_t strideofFloatCount = 2 * kFloatPerVector;
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 1, 1.0f, 0.0f, 0.0f, 0.0f, 4,
                       1, 4, 1.0f, 1.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::white, GLColor::white, GLColor::white, GLColor::white);
    
        setArrayValues(floatData, 0, arraySize, strideofFloatCount, 0, 1, 1, 1.0f, 0.0f, 0.0f, 0.0f, 4,
                       1, 4, 0.0f, 0.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::red, GLColor::red, GLColor::red);
    
        setArrayValues(floatData, arraySize / 4, arraySize / 2, strideofFloatCount, 0, 1, 1, 0.0f, 0.0f,
                       0.0f, 0.0f, 4, 1, 4, 0.0f, 1.0f, 1.0f, 0.0f);
        glBufferSubData(GL_UNIFORM_BUFFER, 0,
                        std::min(static_cast<size_t>(blockSize), floatCount * sizeof(GLfloat)),
                        floatData.data());
        drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
        checkResults(GLColor::red, GLColor::blue, GLColor::red, GLColor::red);
    }
    
    // Test to transfer a uniform block large array member as an actual parameter to a function.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberAsActualParameter)
    {
        ANGLE_SKIP_TEST_IF(IsAdreno());
    
        constexpr char kVS[] = R"(#version 300 es
    layout(location=0) in vec3 a_position;
    
    layout(std140) uniform UBO1{
        mat4x4 buf1[90];
    } instance;
    
    layout(std140) uniform UBO2{
        mat4x4 buf2[90];
    };
    
    vec4 test(mat4x4[90] para1, mat4x4[90] para2, vec3 pos){
        return para1[0] * para2[0] * vec4(pos, 1.0);
    }
    
    void main(void){
        gl_Position = test(instance.buf1, buf2, a_position);
    })";
    
        constexpr char kFS[] = R"(#version 300 es
    precision mediump float;
    
    uniform vec3 u_color;
    out vec4 oFragColor;
    
    void main(void){
        oFragColor = vec4( u_color, 1.0);
    })";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test array operators to operate on uniform block large array member.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, MemberArrayOperations)
    {
        ANGLE_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES());
    
        constexpr char kVS[] = R"(#version 300 es
    layout(location=0) in vec3 a_position;
    
    layout(std140) uniform UBO1{
        mat4x4 buf1[90];
    };
    
    layout(std140) uniform UBO2{
        mat4x4 buf2[90];
    };
    
    layout(std140) uniform UBO3{
        mat4x4 buf[90];
    } instance;
    
    vec4 test1( mat4x4[90] para, vec3 pos ){
        return para[ 0 ] * vec4( pos, 1.0 );
    }
    
    mat4x4[90] test2()
    {
        return instance.buf;
    }
    
    void main(void){
        if (buf1 == buf2)
        {
            mat4x4 temp1[90] = buf1;
            gl_Position = test1(temp1, a_position);
        }
        else
        {
            mat4x4 temp2[90] = test2();
            gl_Position = test1(temp2, a_position);
        }
    })";
    
        constexpr char kFS[] = R"(#version 300 es
    precision mediump float;
    
    uniform vec3 u_color;
    out vec4 oFragColor;
    
    void main(void){
        oFragColor = vec4( u_color, 1.0);
    })";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test to throw a warning if a uniform block with a large array member
    // fails to hit the optimization on D3D backend.
    TEST_P(UniformBlockWithOneLargeArrayMemberTest, ThrowPerfWarningInD3D)
    {
        constexpr char kFS[] = R"(#version 300 es
    precision highp float;
    
    struct S1 {
        vec2 a[2];
    };
    
    struct S2 {
        mat2x4 b;
    };
    
    layout(std140, row_major) uniform UBO1{
        mat3x2 buf1[128];
    };
    
    layout(std140, row_major) uniform UBO2{
        mat4x3 buf2[128];
    } instance1;
    
    layout(std140, row_major) uniform UBO3{
        S1 buf3[128];
    };
    
    layout(std140, row_major) uniform UBO4{
        S2 buf4[128];
    } instance2[2];
    
    out vec4 my_FragColor;
    
    void main(void){
        uvec2 coord = uvec2(floor(gl_FragCoord.xy));
        uint x = coord.x % 64u;
        uint y = coord.y;
        my_FragColor = vec4(buf1[y]*instance1.buf2[y]*instance2[0].buf4[y].b*buf3[y].a[x], 0.0f, 1.0);
    
    })";
    
        ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
        EXPECT_GL_NO_ERROR();
    }
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UniformBufferTest);
    ANGLE_INSTANTIATE_TEST_ES3(UniformBufferTest);
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UniformBlockWithOneLargeArrayMemberTest);
    ANGLE_INSTANTIATE_TEST_ES3(UniformBlockWithOneLargeArrayMemberTest);
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UniformBufferTest31);
    ANGLE_INSTANTIATE_TEST_ES31(UniformBufferTest31);
    
    }  // namespace