• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // RobustBufferAccessBehaviorTest:
7 //   Various tests related for GL_KHR_robust_buffer_access_behavior.
8 //
9 
10 #include "test_utils/ANGLETest.h"
11 #include "test_utils/gl_raii.h"
12 #include "util/EGLWindow.h"
13 
14 #include <array>
15 
16 using namespace angle;
17 
18 namespace
19 {
20 
21 class RobustBufferAccessBehaviorTest : public ANGLETest
22 {
23   protected:
RobustBufferAccessBehaviorTest()24     RobustBufferAccessBehaviorTest() : mProgram(0), mTestAttrib(-1)
25     {
26         setWindowWidth(128);
27         setWindowHeight(128);
28         setConfigRedBits(8);
29         setConfigGreenBits(8);
30         setConfigBlueBits(8);
31         setConfigAlphaBits(8);
32 
33         // Test flakiness was noticed when reusing displays.
34         forceNewDisplay();
35     }
36 
testTearDown()37     void testTearDown() override { glDeleteProgram(mProgram); }
38 
initExtension()39     bool initExtension()
40     {
41         EGLWindow *window  = getEGLWindow();
42         EGLDisplay display = window->getDisplay();
43         if (!IsEGLDisplayExtensionEnabled(display, "EGL_EXT_create_context_robustness"))
44         {
45             return false;
46         }
47         setRobustAccess(true);
48         if (!IsGLExtensionEnabled("GL_KHR_robust_buffer_access_behavior"))
49         {
50             return false;
51         }
52         return true;
53     }
54 
initBasicProgram()55     void initBasicProgram()
56     {
57         constexpr char kVS[] =
58             "precision mediump float;\n"
59             "attribute vec4 position;\n"
60             "attribute vec4 vecRandom;\n"
61             "varying vec4 v_color;\n"
62             "bool testFloatComponent(float component) {\n"
63             "    return (component == 0.2 || component == 0.0);\n"
64             "}\n"
65             "bool testLastFloatComponent(float component) {\n"
66             "    return testFloatComponent(component) || component == 1.0;\n"
67             "}\n"
68             "void main() {\n"
69             "    if (testFloatComponent(vecRandom.x) &&\n"
70             "        testFloatComponent(vecRandom.y) &&\n"
71             "        testFloatComponent(vecRandom.z) &&\n"
72             "        testLastFloatComponent(vecRandom.w)) {\n"
73             "        v_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
74             "    } else {\n"
75             "        v_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
76             "    }\n"
77             "    gl_Position = position;\n"
78             "}\n";
79 
80         constexpr char kFS[] =
81             "precision mediump float;\n"
82             "varying vec4 v_color;\n"
83             "void main() {\n"
84             "    gl_FragColor = v_color;\n"
85             "}\n";
86 
87         mProgram = CompileProgram(kVS, kFS);
88         ASSERT_NE(0u, mProgram);
89 
90         mTestAttrib = glGetAttribLocation(mProgram, "vecRandom");
91         ASSERT_NE(-1, mTestAttrib);
92 
93         glUseProgram(mProgram);
94     }
95 
runIndexOutOfRangeTests(GLenum drawType)96     void runIndexOutOfRangeTests(GLenum drawType)
97     {
98         if (mProgram == 0)
99         {
100             initBasicProgram();
101         }
102 
103         GLBuffer bufferIncomplete;
104         glBindBuffer(GL_ARRAY_BUFFER, bufferIncomplete);
105         std::array<GLfloat, 12> randomData = {
106             {0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f}};
107         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * randomData.size(), randomData.data(),
108                      drawType);
109 
110         glEnableVertexAttribArray(mTestAttrib);
111         glVertexAttribPointer(mTestAttrib, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
112 
113         glClearColor(0.0, 0.0, 1.0, 1.0);
114         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
115 
116         drawIndexedQuad(mProgram, "position", 0.5f);
117 
118         int width     = getWindowWidth();
119         int height    = getWindowHeight();
120         GLenum result = glGetError();
121         // For D3D dynamic draw, we still return invalid operation. Once we force the index buffer
122         // to clamp any out of range indices instead of invalid operation, this part can be removed.
123         // We can always get GL_NO_ERROR.
124         if (result == GL_INVALID_OPERATION)
125         {
126             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::blue);
127             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::blue);
128             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::blue);
129             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::blue);
130         }
131         else
132         {
133             EXPECT_GLENUM_EQ(GL_NO_ERROR, result);
134             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::green);
135             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::green);
136             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::green);
137             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::green);
138         }
139     }
140 
141     GLuint mProgram;
142     GLint mTestAttrib;
143 };
144 
145 // Test that static draw with out-of-bounds reads will not read outside of the data store of the
146 // buffer object and will not result in GL interruption or termination when
147 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithStaticDraw)148 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithStaticDraw)
149 {
150     ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
151 
152     // Failing on NV after changing shard count of angle_end2end_tests. http://anglebug.com/2799
153     // Also failing on AMD after a validation change. http://anglebug.com/3042
154     ANGLE_SKIP_TEST_IF(IsD3D11_FL93());
155 
156     ANGLE_SKIP_TEST_IF(!initExtension());
157 
158     runIndexOutOfRangeTests(GL_STATIC_DRAW);
159 }
160 
161 // Test that dynamic draw with out-of-bounds reads will not read outside of the data store of the
162 // buffer object and will not result in GL interruption or termination when
163 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithDynamicDraw)164 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithDynamicDraw)
165 {
166     ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
167     ANGLE_SKIP_TEST_IF(!initExtension());
168 
169     runIndexOutOfRangeTests(GL_DYNAMIC_DRAW);
170 }
171 
172 // Test that vertex buffers are rebound with the correct offsets in subsequent calls in the D3D11
173 // backend.  http://crbug.com/837002
TEST_P(RobustBufferAccessBehaviorTest,D3D11StateSynchronizationOrderBug)174 TEST_P(RobustBufferAccessBehaviorTest, D3D11StateSynchronizationOrderBug)
175 {
176     ANGLE_SKIP_TEST_IF(!initExtension());
177 
178     glDisable(GL_DEPTH_TEST);
179 
180     // 2 quads, the first one red, the second one green
181     const std::array<angle::Vector4, 16> vertices{
182         angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v0
183         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c0
184         angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v1
185         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c1
186         angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v2
187         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c2
188         angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v3
189         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c3
190 
191         angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v4
192         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c4
193         angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v5
194         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c5
195         angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v6
196         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c6
197         angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v7
198         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c7
199     };
200 
201     GLBuffer vb;
202     glBindBuffer(GL_ARRAY_BUFFER, vb);
203     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
204 
205     const std::array<GLushort, 12> indicies{
206         0, 1, 2, 0, 2, 3,  // quad0
207         4, 5, 6, 4, 6, 7,  // quad1
208     };
209 
210     GLBuffer ib;
211     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
212     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies.data(), GL_STATIC_DRAW);
213 
214     constexpr char kVS[] = R"(
215 precision highp float;
216 attribute vec4 a_position;
217 attribute vec4 a_color;
218 
219 varying vec4 v_color;
220 
221 void main()
222 {
223     gl_Position = a_position;
224     v_color = a_color;
225 })";
226 
227     constexpr char kFS[] = R"(
228 precision highp float;
229 varying vec4 v_color;
230 
231 void main()
232 {
233     gl_FragColor = v_color;
234 })";
235 
236     ANGLE_GL_PROGRAM(program, kVS, kFS);
237     glUseProgram(program);
238 
239     GLint positionLocation = glGetAttribLocation(program, "a_position");
240     glEnableVertexAttribArray(positionLocation);
241     glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2, 0);
242 
243     GLint colorLocation = glGetAttribLocation(program, "a_color");
244     glEnableVertexAttribArray(colorLocation);
245     glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2,
246                           reinterpret_cast<const void *>(sizeof(angle::Vector4)));
247 
248     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
249     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
250 
251     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
252                    reinterpret_cast<const void *>(sizeof(GLshort) * 6));
253     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
254 
255     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
256     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
257 
258     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
259                    reinterpret_cast<const void *>(sizeof(GLshort) * 6));
260     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
261 }
262 
263 // Covers drawing with a very large vertex range which overflows GLsizei. http://crbug.com/842028
TEST_P(RobustBufferAccessBehaviorTest,VeryLargeVertexCountWithDynamicVertexData)264 TEST_P(RobustBufferAccessBehaviorTest, VeryLargeVertexCountWithDynamicVertexData)
265 {
266     ANGLE_SKIP_TEST_IF(!initExtension());
267     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_element_index_uint"));
268 
269     constexpr GLsizei kIndexCount           = 32;
270     std::array<GLuint, kIndexCount> indices = {{}};
271     for (GLsizei index = 0; index < kIndexCount; ++index)
272     {
273         indices[index] = ((std::numeric_limits<GLuint>::max() - 2) / kIndexCount) * index;
274     }
275 
276     GLBuffer indexBuffer;
277     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
278     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(),
279                  GL_STATIC_DRAW);
280 
281     std::array<GLfloat, 256> vertexData = {{}};
282 
283     GLBuffer vertexBuffer;
284     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
285     glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(GLfloat), vertexData.data(),
286                  GL_DYNAMIC_DRAW);
287 
288     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
289     glUseProgram(program);
290 
291     GLint attribLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
292     ASSERT_NE(-1, attribLoc);
293 
294     glVertexAttribPointer(attribLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
295     glEnableVertexAttribArray(attribLoc);
296     ASSERT_GL_NO_ERROR();
297 
298     glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_INT, nullptr);
299 
300     // This may or may not generate an error, but it should not crash.
301 }
302 
303 // Test that robust access works even if there's no data uploaded to the vertex buffer at all.
TEST_P(RobustBufferAccessBehaviorTest,NoBufferData)304 TEST_P(RobustBufferAccessBehaviorTest, NoBufferData)
305 {
306     // http://crbug.com/889303: Possible driver bug on NVIDIA Shield TV.
307     // http://anglebug.com/2861: Fails abnormally on Android
308     ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
309 
310     ANGLE_SKIP_TEST_IF(!initExtension());
311     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
312     glUseProgram(program);
313 
314     glEnableVertexAttribArray(0);
315     GLBuffer buf;
316     glBindBuffer(GL_ARRAY_BUFFER, buf);
317 
318     glVertexAttribPointer(0, 1, GL_FLOAT, false, 0, nullptr);
319     ASSERT_GL_NO_ERROR();
320 
321     std::array<GLubyte, 1u> indices = {0};
322     glDrawElements(GL_POINTS, indices.size(), GL_UNSIGNED_BYTE, indices.data());
323     ASSERT_GL_NO_ERROR();
324 }
325 
326 constexpr char kWebGLVS[] = R"(attribute vec2 position;
327 attribute vec4 aOne;
328 attribute vec4 aTwo;
329 varying vec4 v;
330 uniform vec2 comparison;
331 
332 bool isRobust(vec4 value) {
333     // The valid buffer range is filled with this value.
334     if (value.xy == comparison)
335         return true;
336     // Checking the w value is a bit complex.
337     return (value.xyz == vec3(0, 0, 0));
338 }
339 
340 void main() {
341     gl_Position = vec4(position, 0, 1);
342     if (isRobust(aOne) && isRobust(aTwo)) {
343         v = vec4(0, 1, 0, 1);
344     } else {
345         v = vec4(1, 0, 0, 1);
346     }
347 })";
348 
349 constexpr char kWebGLFS[] = R"(precision mediump float;
350 varying vec4 v;
351 void main() {
352     gl_FragColor = v;
353 })";
354 
355 // Test buffer with interleaved (3+2) float vectors. Adapted from WebGL test
356 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,InterleavedAttributes)357 TEST_P(RobustBufferAccessBehaviorTest, InterleavedAttributes)
358 {
359     ANGLE_SKIP_TEST_IF(!initExtension());
360 
361     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
362     glUseProgram(program);
363 
364     constexpr GLint kPosLoc = 0;
365     constexpr GLint kOneLoc = 1;
366     constexpr GLint kTwoLoc = 2;
367 
368     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
369     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
370     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
371 
372     // Create a buffer of 200 valid sets of quad lists.
373     constexpr size_t kNumQuads = 200;
374     using QuadVerts            = std::array<Vector3, 6>;
375     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
376 
377     GLBuffer positionBuf;
378     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
379     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
380     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
381     glEnableVertexAttribArray(kPosLoc);
382 
383     constexpr GLfloat kDefaultFloat = 0.2f;
384     std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
385 
386     GLBuffer vbo;
387     glBindBuffer(GL_ARRAY_BUFFER, vbo);
388 
389     // enough for 9 vertices, so 3 triangles
390     glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_STATIC_DRAW);
391 
392     // bind first 3 elements, with a stride of 5 float elements
393     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
394     // bind 2 elements, starting after the first 3; same stride of 5 float elements
395     glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
396                           reinterpret_cast<const GLvoid *>(3 * 4));
397 
398     glEnableVertexAttribArray(kOneLoc);
399     glEnableVertexAttribArray(kTwoLoc);
400 
401     // set test uniform
402     GLint uniLoc = glGetUniformLocation(program, "comparison");
403     ASSERT_NE(-1, uniLoc);
404     glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
405 
406     // Draw out of bounds.
407     glDrawArrays(GL_TRIANGLES, 0, 10000);
408     GLenum err = glGetError();
409     if (err == GL_NO_ERROR)
410     {
411         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
412     }
413     else
414     {
415         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
416     }
417 
418     glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
419     err = glGetError();
420     if (err == GL_NO_ERROR)
421     {
422         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
423     }
424     else
425     {
426         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
427     }
428 }
429 
430 // Tests redefining an empty buffer. Adapted from WebGL test
431 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,EmptyBuffer)432 TEST_P(RobustBufferAccessBehaviorTest, EmptyBuffer)
433 {
434     ANGLE_SKIP_TEST_IF(!initExtension());
435 
436     // AMD GL does not support robustness. http://anglebug.com/3099
437     ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
438 
439     // http://anglebug.com/2861: Fails abnormally on Android
440     ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
441 
442     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
443     glUseProgram(program);
444 
445     constexpr GLint kPosLoc = 0;
446     constexpr GLint kOneLoc = 1;
447     constexpr GLint kTwoLoc = 2;
448 
449     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
450     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
451     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
452 
453     // Create a buffer of 200 valid sets of quad lists.
454     constexpr size_t kNumQuads = 200;
455     using QuadVerts            = std::array<Vector3, 6>;
456     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
457 
458     GLBuffer positionBuf;
459     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
460     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
461     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
462     glEnableVertexAttribArray(kPosLoc);
463 
464     // set test uniform
465     GLint uniLoc = glGetUniformLocation(program, "comparison");
466     ASSERT_NE(-1, uniLoc);
467     glUniform2f(uniLoc, 0, 0);
468 
469     // Define empty buffer.
470     GLBuffer buffer;
471     glBindBuffer(GL_ARRAY_BUFFER, buffer);
472     glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
473     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
474     glEnableVertexAttribArray(kOneLoc);
475     glDrawArrays(GL_TRIANGLES, 0, 3);
476     GLenum err = glGetError();
477     if (err == GL_NO_ERROR)
478     {
479         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
480     }
481     else
482     {
483         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
484     }
485 
486     // Redefine buffer with 3 float vectors.
487     constexpr GLfloat kFloats[] = {0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0};
488     glBufferData(GL_ARRAY_BUFFER, sizeof(kFloats), kFloats, GL_STATIC_DRAW);
489     glDrawArrays(GL_TRIANGLES, 0, 3);
490     ASSERT_GL_NO_ERROR();
491 }
492 
493 // Tests robust buffer access with dynamic buffer usage.
TEST_P(RobustBufferAccessBehaviorTest,DynamicBuffer)494 TEST_P(RobustBufferAccessBehaviorTest, DynamicBuffer)
495 {
496     ANGLE_SKIP_TEST_IF(!initExtension());
497 
498     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
499     glUseProgram(program);
500 
501     constexpr GLint kPosLoc = 0;
502     constexpr GLint kOneLoc = 1;
503     constexpr GLint kTwoLoc = 2;
504 
505     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
506     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
507     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
508 
509     // Create a buffer of 200 valid sets of quad lists.
510     constexpr size_t kNumQuads = 200;
511     using QuadVerts            = std::array<Vector3, 6>;
512     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
513 
514     GLBuffer positionBuf;
515     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
516     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
517     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
518     glEnableVertexAttribArray(kPosLoc);
519 
520     constexpr GLfloat kDefaultFloat = 0.2f;
521     std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
522 
523     GLBuffer vbo;
524     glBindBuffer(GL_ARRAY_BUFFER, vbo);
525 
526     // enough for 9 vertices, so 3 triangles
527     glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_DYNAMIC_DRAW);
528 
529     // bind first 3 elements, with a stride of 5 float elements
530     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
531     // bind 2 elements, starting after the first 3; same stride of 5 float elements
532     glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
533                           reinterpret_cast<const GLvoid *>(3 * 4));
534 
535     glEnableVertexAttribArray(kOneLoc);
536     glEnableVertexAttribArray(kTwoLoc);
537 
538     // set test uniform
539     GLint uniLoc = glGetUniformLocation(program, "comparison");
540     ASSERT_NE(-1, uniLoc);
541     glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
542 
543     // Draw out of bounds.
544     glDrawArrays(GL_TRIANGLES, 0, 10000);
545     GLenum err = glGetError();
546     if (err == GL_NO_ERROR)
547     {
548         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
549     }
550     else
551     {
552         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
553     }
554 
555     glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
556     err = glGetError();
557     if (err == GL_NO_ERROR)
558     {
559         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
560     }
561     else
562     {
563         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
564     }
565 }
566 
567 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND_ES31(RobustBufferAccessBehaviorTest);
568 
569 }  // namespace
570