Edit

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

Branch :

  • Show log

    Commit

  • Author : Yuly Novikov
    Date : 2021-03-02 19:04:57
    Hash : a6b16d29
    Message : Suppress UNINSTANTIATED_PARAMETERIZED_TEST failures on Ozone We only support ES2 on Ozone, so tests that depend on ES3 or ES31 support are not instantiated there. Bug: chromium:1183147 Change-Id: Id58bcd9b44a5b9a70b5ae8115e27c44f5dc81226 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2726550 Reviewed-by: Jonah Ryan-Davis <jonahr@google.com> Commit-Queue: Yuly Novikov <ynovikov@chromium.org>

  • 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 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