Edit

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

Branch :

  • Show log

    Commit

  • Author : Jonah Ryan-Davis
    Date : 2019-03-19 11:10:15
    Hash : 9078c6a7
    Message : Update necessary angle_end2end_tests to check the Android device name Some tests fail only on specific devices, so the tests should be updated to reflect that, and allow more tests to run on more devices. Bug: angleproject:3275 Change-Id: I8e3183c1769c0bb8ed6d2605afcaf399cb1d9ed0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1534463 Reviewed-by: Geoff Lang <geofflang@chromium.org> Commit-Queue: Jonah Ryan-Davis <jonahr@google.com>

  • src/tests/gl_tests/RobustBufferAccessBehaviorTest.cpp
  • //
    // Copyright 2017 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // RobustBufferAccessBehaviorTest:
    //   Various tests related for GL_KHR_robust_buffer_access_behavior.
    //
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    #include "util/EGLWindow.h"
    
    #include <array>
    
    using namespace angle;
    
    namespace
    {
    
    class RobustBufferAccessBehaviorTest : public ANGLETest
    {
      protected:
        RobustBufferAccessBehaviorTest() : mProgram(0), mTestAttrib(-1)
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
    
            // Test flakiness was noticed when reusing displays.
            forceNewDisplay();
        }
    
        void TearDown() override
        {
            glDeleteProgram(mProgram);
            ANGLETest::TearDown();
        }
    
        bool initExtension()
        {
            EGLWindow *window  = getEGLWindow();
            EGLDisplay display = window->getDisplay();
            if (!eglDisplayExtensionEnabled(display, "EGL_EXT_create_context_robustness"))
            {
                return false;
            }
    
            ANGLETest::TearDown();
            setRobustAccess(true);
            ANGLETest::SetUp();
            if (!extensionEnabled("GL_KHR_robust_buffer_access_behavior"))
            {
                return false;
            }
            return true;
        }
    
        void initBasicProgram()
        {
            constexpr char kVS[] =
                "precision mediump float;\n"
                "attribute vec4 position;\n"
                "attribute vec4 vecRandom;\n"
                "varying vec4 v_color;\n"
                "bool testFloatComponent(float component) {\n"
                "    return (component == 0.2 || component == 0.0);\n"
                "}\n"
                "bool testLastFloatComponent(float component) {\n"
                "    return testFloatComponent(component) || component == 1.0;\n"
                "}\n"
                "void main() {\n"
                "    if (testFloatComponent(vecRandom.x) &&\n"
                "        testFloatComponent(vecRandom.y) &&\n"
                "        testFloatComponent(vecRandom.z) &&\n"
                "        testLastFloatComponent(vecRandom.w)) {\n"
                "        v_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
                "    } else {\n"
                "        v_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
                "    }\n"
                "    gl_Position = position;\n"
                "}\n";
    
            constexpr char kFS[] =
                "precision mediump float;\n"
                "varying vec4 v_color;\n"
                "void main() {\n"
                "    gl_FragColor = v_color;\n"
                "}\n";
    
            mProgram = CompileProgram(kVS, kFS);
            ASSERT_NE(0u, mProgram);
    
            mTestAttrib = glGetAttribLocation(mProgram, "vecRandom");
            ASSERT_NE(-1, mTestAttrib);
    
            glUseProgram(mProgram);
        }
    
        void runIndexOutOfRangeTests(GLenum drawType)
        {
            if (mProgram == 0)
            {
                initBasicProgram();
            }
    
            GLBuffer bufferIncomplete;
            glBindBuffer(GL_ARRAY_BUFFER, bufferIncomplete);
            std::array<GLfloat, 12> randomData = {
                {0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f}};
            glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * randomData.size(), randomData.data(),
                         drawType);
    
            glEnableVertexAttribArray(mTestAttrib);
            glVertexAttribPointer(mTestAttrib, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
    
            glClearColor(0.0, 0.0, 1.0, 1.0);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            drawIndexedQuad(mProgram, "position", 0.5f);
    
            int width     = getWindowWidth();
            int height    = getWindowHeight();
            GLenum result = glGetError();
            // For D3D dynamic draw, we still return invalid operation. Once we force the index buffer
            // to clamp any out of range indices instead of invalid operation, this part can be removed.
            // We can always get GL_NO_ERROR.
            if (result == GL_INVALID_OPERATION)
            {
                EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::blue);
                EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::blue);
                EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::blue);
                EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::blue);
            }
            else
            {
                EXPECT_GLENUM_EQ(GL_NO_ERROR, result);
                EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::green);
                EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::green);
                EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::green);
                EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::green);
            }
        }
    
        GLuint mProgram;
        GLint mTestAttrib;
    };
    
    // Test that static draw with out-of-bounds reads will not read outside of the data store of the
    // buffer object and will not result in GL interruption or termination when
    // GL_KHR_robust_buffer_access_behavior is supported.
    TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithStaticDraw)
    {
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
    
        // Failing on NV after changing shard count of angle_end2end_tests. http://anglebug.com/2799
        // Also failing on AMD after a validation change. http://anglebug.com/3042
        ANGLE_SKIP_TEST_IF(IsD3D11_FL93());
    
        ANGLE_SKIP_TEST_IF(!initExtension());
    
        runIndexOutOfRangeTests(GL_STATIC_DRAW);
    }
    
    // Test that dynamic draw with out-of-bounds reads will not read outside of the data store of the
    // buffer object and will not result in GL interruption or termination when
    // GL_KHR_robust_buffer_access_behavior is supported.
    TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithDynamicDraw)
    {
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
        ANGLE_SKIP_TEST_IF(!initExtension());
    
        runIndexOutOfRangeTests(GL_DYNAMIC_DRAW);
    }
    
    // Test that vertex buffers are rebound with the correct offsets in subsequent calls in the D3D11
    // backend.  http://crbug.com/837002
    TEST_P(RobustBufferAccessBehaviorTest, D3D11StateSynchronizationOrderBug)
    {
        ANGLE_SKIP_TEST_IF(!initExtension());
    
        glDisable(GL_DEPTH_TEST);
    
        // 2 quads, the first one red, the second one green
        const std::array<angle::Vector4, 16> vertices{
            angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v0
            angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c0
            angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v1
            angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c1
            angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v2
            angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c2
            angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v3
            angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c3
    
            angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v4
            angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c4
            angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v5
            angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c5
            angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v6
            angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c6
            angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v7
            angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c7
        };
    
        GLBuffer vb;
        glBindBuffer(GL_ARRAY_BUFFER, vb);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
    
        const std::array<GLushort, 12> indicies{
            0, 1, 2, 0, 2, 3,  // quad0
            4, 5, 6, 4, 6, 7,  // quad1
        };
    
        GLBuffer ib;
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies.data(), GL_STATIC_DRAW);
    
        constexpr char kVS[] = R"(
    precision highp float;
    attribute vec4 a_position;
    attribute vec4 a_color;
    
    varying vec4 v_color;
    
    void main()
    {
        gl_Position = a_position;
        v_color = a_color;
    })";
    
        constexpr char kFS[] = R"(
    precision highp float;
    varying vec4 v_color;
    
    void main()
    {
        gl_FragColor = v_color;
    })";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        glUseProgram(program);
    
        GLint positionLocation = glGetAttribLocation(program, "a_position");
        glEnableVertexAttribArray(positionLocation);
        glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2, 0);
    
        GLint colorLocation = glGetAttribLocation(program, "a_color");
        glEnableVertexAttribArray(colorLocation);
        glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2,
                              reinterpret_cast<const void *>(sizeof(angle::Vector4)));
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
                       reinterpret_cast<const void *>(sizeof(GLshort) * 6));
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
                       reinterpret_cast<const void *>(sizeof(GLshort) * 6));
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Covers drawing with a very large vertex range which overflows GLsizei. http://crbug.com/842028
    TEST_P(RobustBufferAccessBehaviorTest, VeryLargeVertexCountWithDynamicVertexData)
    {
        ANGLE_SKIP_TEST_IF(!initExtension());
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_element_index_uint"));
    
        constexpr GLsizei kIndexCount           = 32;
        std::array<GLuint, kIndexCount> indices = {{}};
        for (GLsizei index = 0; index < kIndexCount; ++index)
        {
            indices[index] = ((std::numeric_limits<GLuint>::max() - 2) / kIndexCount) * index;
        }
    
        GLBuffer indexBuffer;
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(),
                     GL_STATIC_DRAW);
    
        std::array<GLfloat, 256> vertexData = {{}};
    
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(GLfloat), vertexData.data(),
                     GL_DYNAMIC_DRAW);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glUseProgram(program);
    
        GLint attribLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, attribLoc);
    
        glVertexAttribPointer(attribLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(attribLoc);
        ASSERT_GL_NO_ERROR();
    
        glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_INT, nullptr);
    
        // This may or may not generate an error, but it should not crash.
    }
    
    // Test that robust access works even if there's no data uploaded to the vertex buffer at all.
    TEST_P(RobustBufferAccessBehaviorTest, NoBufferData)
    {
        // http://crbug.com/889303: Possible driver bug on NVIDIA Shield TV.
        // http://anglebug.com/2861: Fails abnormally on Android
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
    
        ANGLE_SKIP_TEST_IF(!initExtension());
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glUseProgram(program);
    
        glEnableVertexAttribArray(0);
        GLBuffer buf;
        glBindBuffer(GL_ARRAY_BUFFER, buf);
    
        glVertexAttribPointer(0, 1, GL_FLOAT, false, 0, nullptr);
        ASSERT_GL_NO_ERROR();
    
        std::array<GLubyte, 1u> indices = {0};
        glDrawElements(GL_POINTS, indices.size(), GL_UNSIGNED_BYTE, indices.data());
        ASSERT_GL_NO_ERROR();
    }
    
    constexpr char kWebGLVS[] = R"(attribute vec2 position;
    attribute vec4 aOne;
    attribute vec4 aTwo;
    varying vec4 v;
    uniform vec2 comparison;
    
    bool isRobust(vec4 value) {
        // The valid buffer range is filled with this value.
        if (value.xy == comparison)
            return true;
        // Checking the w value is a bit complex.
        return (value.xyz == vec3(0, 0, 0));
    }
    
    void main() {
        gl_Position = vec4(position, 0, 1);
        if (isRobust(aOne) && isRobust(aTwo)) {
            v = vec4(0, 1, 0, 1);
        } else {
            v = vec4(1, 0, 0, 1);
        }
    })";
    
    constexpr char kWebGLFS[] = R"(precision mediump float;
    varying vec4 v;
    void main() {
        gl_FragColor = v;
    })";
    
    // Test buffer with interleaved (3+2) float vectors. Adapted from WebGL test
    // conformance/rendering/draw-arrays-out-of-bounds.html
    TEST_P(RobustBufferAccessBehaviorTest, InterleavedAttributes)
    {
        ANGLE_SKIP_TEST_IF(!initExtension());
    
        ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
        glUseProgram(program);
    
        constexpr GLint kPosLoc = 0;
        constexpr GLint kOneLoc = 1;
        constexpr GLint kTwoLoc = 2;
    
        ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
        ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
        ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
    
        // Create a buffer of 200 valid sets of quad lists.
        constexpr size_t kNumQuads = 200;
        using QuadVerts            = std::array<Vector3, 6>;
        std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
    
        GLBuffer positionBuf;
        glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
        glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
        glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(kPosLoc);
    
        constexpr GLfloat kDefaultFloat = 0.2f;
        std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
    
        GLBuffer vbo;
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
    
        // enough for 9 vertices, so 3 triangles
        glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_STATIC_DRAW);
    
        // bind first 3 elements, with a stride of 5 float elements
        glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
        // bind 2 elements, starting after the first 3; same stride of 5 float elements
        glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
                              reinterpret_cast<const GLvoid *>(3 * 4));
    
        glEnableVertexAttribArray(kOneLoc);
        glEnableVertexAttribArray(kTwoLoc);
    
        // set test uniform
        GLint uniLoc = glGetUniformLocation(program, "comparison");
        ASSERT_NE(-1, uniLoc);
        glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
    
        // Draw out of bounds.
        glDrawArrays(GL_TRIANGLES, 0, 10000);
        GLenum err = glGetError();
        if (err == GL_NO_ERROR)
        {
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        }
        else
        {
            EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
        }
    
        glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
        err = glGetError();
        if (err == GL_NO_ERROR)
        {
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        }
        else
        {
            EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
        }
    }
    
    // Tests redefining an empty buffer. Adapted from WebGL test
    // conformance/rendering/draw-arrays-out-of-bounds.html
    TEST_P(RobustBufferAccessBehaviorTest, EmptyBuffer)
    {
        ANGLE_SKIP_TEST_IF(!initExtension());
    
        // AMD GL does not support robustness. http://anglebug.com/3099
        ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
    
        // http://anglebug.com/2861: Fails abnormally on Android
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
    
        ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
        glUseProgram(program);
    
        constexpr GLint kPosLoc = 0;
        constexpr GLint kOneLoc = 1;
        constexpr GLint kTwoLoc = 2;
    
        ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
        ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
        ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
    
        // Create a buffer of 200 valid sets of quad lists.
        constexpr size_t kNumQuads = 200;
        using QuadVerts            = std::array<Vector3, 6>;
        std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
    
        GLBuffer positionBuf;
        glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
        glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
        glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(kPosLoc);
    
        // set test uniform
        GLint uniLoc = glGetUniformLocation(program, "comparison");
        ASSERT_NE(-1, uniLoc);
        glUniform2f(uniLoc, 0, 0);
    
        // Define empty buffer.
        GLBuffer buffer;
        glBindBuffer(GL_ARRAY_BUFFER, buffer);
        glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
        glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(kOneLoc);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        GLenum err = glGetError();
        if (err == GL_NO_ERROR)
        {
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        }
        else
        {
            EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
        }
    
        // Redefine buffer with 3 float vectors.
        constexpr GLfloat kFloats[] = {0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0};
        glBufferData(GL_ARRAY_BUFFER, sizeof(kFloats), kFloats, GL_STATIC_DRAW);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        ASSERT_GL_NO_ERROR();
    }
    
    // Tests robust buffer access with dynamic buffer usage.
    TEST_P(RobustBufferAccessBehaviorTest, DynamicBuffer)
    {
        ANGLE_SKIP_TEST_IF(!initExtension());
    
        ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
        glUseProgram(program);
    
        constexpr GLint kPosLoc = 0;
        constexpr GLint kOneLoc = 1;
        constexpr GLint kTwoLoc = 2;
    
        ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
        ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
        ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
    
        // Create a buffer of 200 valid sets of quad lists.
        constexpr size_t kNumQuads = 200;
        using QuadVerts            = std::array<Vector3, 6>;
        std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
    
        GLBuffer positionBuf;
        glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
        glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
        glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(kPosLoc);
    
        constexpr GLfloat kDefaultFloat = 0.2f;
        std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
    
        GLBuffer vbo;
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
    
        // enough for 9 vertices, so 3 triangles
        glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_DYNAMIC_DRAW);
    
        // bind first 3 elements, with a stride of 5 float elements
        glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
        // bind 2 elements, starting after the first 3; same stride of 5 float elements
        glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
                              reinterpret_cast<const GLvoid *>(3 * 4));
    
        glEnableVertexAttribArray(kOneLoc);
        glEnableVertexAttribArray(kTwoLoc);
    
        // set test uniform
        GLint uniLoc = glGetUniformLocation(program, "comparison");
        ASSERT_NE(-1, uniLoc);
        glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
    
        // Draw out of bounds.
        glDrawArrays(GL_TRIANGLES, 0, 10000);
        GLenum err = glGetError();
        if (err == GL_NO_ERROR)
        {
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        }
        else
        {
            EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
        }
    
        glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
        err = glGetError();
        if (err == GL_NO_ERROR)
        {
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        }
        else
        {
            EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
        }
    }
    
    ANGLE_INSTANTIATE_TEST(RobustBufferAccessBehaviorTest,
                           ES2_D3D9(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D11(),
                           ES3_D3D11(),
                           ES31_D3D11(),
                           ES2_OPENGL(),
                           ES3_OPENGL(),
                           ES31_OPENGL(),
                           ES2_OPENGLES(),
                           ES3_OPENGLES(),
                           ES31_OPENGLES(),
                           ES2_VULKAN());
    
    }  // namespace