xref: /aosp_15_r20/external/angle/src/tests/gl_tests/RobustBufferAccessBehaviorTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 #include "util/OSWindow.h"
14 
15 #include <array>
16 
17 using namespace angle;
18 
19 namespace
20 {
21 
22 class RobustBufferAccessBehaviorTest : public ANGLETest<>
23 {
24   protected:
RobustBufferAccessBehaviorTest()25     RobustBufferAccessBehaviorTest()
26     {
27         setWindowWidth(128);
28         setWindowHeight(128);
29     }
30 
testTearDown()31     void testTearDown() override
32     {
33         glDeleteProgram(mProgram);
34         EGLWindow::Delete(&mEGLWindow);
35         OSWindow::Delete(&mOSWindow);
36     }
37 
initExtension()38     bool initExtension()
39     {
40         mOSWindow = OSWindow::New();
41         if (!mOSWindow->initialize("RobustBufferAccessBehaviorTest", getWindowWidth(),
42                                    getWindowHeight()))
43         {
44             return false;
45         }
46         setWindowVisible(mOSWindow, true);
47 
48         Library *driverLib = ANGLETestEnvironment::GetDriverLibrary(GLESDriverType::AngleEGL);
49 
50         const PlatformParameters &params = GetParam();
51         mEGLWindow                       = EGLWindow::New(params.majorVersion, params.minorVersion);
52         if (!mEGLWindow->initializeDisplay(mOSWindow, driverLib, GLESDriverType::AngleEGL,
53                                            GetParam().eglParameters))
54         {
55             return false;
56         }
57 
58         EGLDisplay display = mEGLWindow->getDisplay();
59         if (!IsEGLDisplayExtensionEnabled(display, "EGL_EXT_create_context_robustness"))
60         {
61             return false;
62         }
63 
64         ConfigParameters configParams;
65         configParams.redBits      = 8;
66         configParams.greenBits    = 8;
67         configParams.blueBits     = 8;
68         configParams.alphaBits    = 8;
69         configParams.robustAccess = true;
70 
71         if (mEGLWindow->initializeSurface(mOSWindow, driverLib, configParams) !=
72             GLWindowResult::NoError)
73         {
74             return false;
75         }
76 
77         if (!mEGLWindow->initializeContext())
78         {
79             return false;
80         }
81 
82         if (!mEGLWindow->makeCurrent())
83         {
84             return false;
85         }
86 
87         if (!IsGLExtensionEnabled("GL_KHR_robust_buffer_access_behavior"))
88         {
89             return false;
90         }
91         return true;
92     }
93 
initBasicProgram()94     void initBasicProgram()
95     {
96         constexpr char kVS[] =
97             "precision mediump float;\n"
98             "attribute vec4 position;\n"
99             "attribute vec4 vecRandom;\n"
100             "varying vec4 v_color;\n"
101             "bool testFloatComponent(float component) {\n"
102             "    return (component == 0.2 || component == 0.0);\n"
103             "}\n"
104             "bool testLastFloatComponent(float component) {\n"
105             "    return testFloatComponent(component) || component == 1.0;\n"
106             "}\n"
107             "void main() {\n"
108             "    if (testFloatComponent(vecRandom.x) &&\n"
109             "        testFloatComponent(vecRandom.y) &&\n"
110             "        testFloatComponent(vecRandom.z) &&\n"
111             "        testLastFloatComponent(vecRandom.w)) {\n"
112             "        v_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
113             "    } else {\n"
114             "        v_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
115             "    }\n"
116             "    gl_Position = position;\n"
117             "}\n";
118 
119         constexpr char kFS[] =
120             "precision mediump float;\n"
121             "varying vec4 v_color;\n"
122             "void main() {\n"
123             "    gl_FragColor = v_color;\n"
124             "}\n";
125 
126         mProgram = CompileProgram(kVS, kFS);
127         ASSERT_NE(0u, mProgram);
128 
129         mTestAttrib = glGetAttribLocation(mProgram, "vecRandom");
130         ASSERT_NE(-1, mTestAttrib);
131 
132         glUseProgram(mProgram);
133     }
134 
runIndexOutOfRangeTests(GLenum drawType)135     void runIndexOutOfRangeTests(GLenum drawType)
136     {
137         if (mProgram == 0)
138         {
139             initBasicProgram();
140         }
141 
142         GLBuffer bufferIncomplete;
143         glBindBuffer(GL_ARRAY_BUFFER, bufferIncomplete);
144         std::array<GLfloat, 12> randomData = {
145             {0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f}};
146         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * randomData.size(), randomData.data(),
147                      drawType);
148 
149         glEnableVertexAttribArray(mTestAttrib);
150         glVertexAttribPointer(mTestAttrib, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
151 
152         glClearColor(0.0, 0.0, 1.0, 1.0);
153         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
154 
155         drawIndexedQuad(mProgram, "position", 0.5f);
156 
157         int width     = getWindowWidth();
158         int height    = getWindowHeight();
159         GLenum result = glGetError();
160         // For D3D dynamic draw, we still return invalid operation. Once we force the index buffer
161         // to clamp any out of range indices instead of invalid operation, this part can be removed.
162         // We can always get GL_NO_ERROR.
163         if (result == GL_INVALID_OPERATION)
164         {
165             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::blue);
166             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::blue);
167             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::blue);
168             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::blue);
169         }
170         else
171         {
172             EXPECT_GLENUM_EQ(GL_NO_ERROR, result);
173             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::green);
174             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::green);
175             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::green);
176             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::green);
177         }
178     }
179 
180     OSWindow *mOSWindow   = nullptr;
181     EGLWindow *mEGLWindow = nullptr;
182     GLuint mProgram       = 0;
183     GLint mTestAttrib     = 0;
184 };
185 
186 // Test that static draw with out-of-bounds reads will not read outside of the data store of the
187 // buffer object and will not result in GL interruption or termination when
188 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithStaticDraw)189 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithStaticDraw)
190 {
191     ANGLE_SKIP_TEST_IF(!initExtension());
192 
193     runIndexOutOfRangeTests(GL_STATIC_DRAW);
194 }
195 
196 // Test that dynamic draw with out-of-bounds reads will not read outside of the data store of the
197 // buffer object and will not result in GL interruption or termination when
198 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithDynamicDraw)199 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithDynamicDraw)
200 {
201     ANGLE_SKIP_TEST_IF(!initExtension());
202 
203     runIndexOutOfRangeTests(GL_DYNAMIC_DRAW);
204 }
205 
206 // Test that vertex buffers are rebound with the correct offsets in subsequent calls in the D3D11
207 // backend.  http://crbug.com/837002
TEST_P(RobustBufferAccessBehaviorTest,D3D11StateSynchronizationOrderBug)208 TEST_P(RobustBufferAccessBehaviorTest, D3D11StateSynchronizationOrderBug)
209 {
210     ANGLE_SKIP_TEST_IF(!initExtension());
211 
212     glDisable(GL_DEPTH_TEST);
213 
214     // 2 quads, the first one red, the second one green
215     const std::array<angle::Vector4, 16> vertices{
216         angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v0
217         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c0
218         angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v1
219         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c1
220         angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v2
221         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c2
222         angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v3
223         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c3
224 
225         angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v4
226         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c4
227         angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v5
228         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c5
229         angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v6
230         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c6
231         angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v7
232         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c7
233     };
234 
235     GLBuffer vb;
236     glBindBuffer(GL_ARRAY_BUFFER, vb);
237     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
238 
239     const std::array<GLushort, 12> indicies{
240         0, 1, 2, 0, 2, 3,  // quad0
241         4, 5, 6, 4, 6, 7,  // quad1
242     };
243 
244     GLBuffer ib;
245     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
246     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies.data(), GL_STATIC_DRAW);
247 
248     constexpr char kVS[] = R"(
249 precision highp float;
250 attribute vec4 a_position;
251 attribute vec4 a_color;
252 
253 varying vec4 v_color;
254 
255 void main()
256 {
257     gl_Position = a_position;
258     v_color = a_color;
259 })";
260 
261     constexpr char kFS[] = R"(
262 precision highp float;
263 varying vec4 v_color;
264 
265 void main()
266 {
267     gl_FragColor = v_color;
268 })";
269 
270     ANGLE_GL_PROGRAM(program, kVS, kFS);
271     glUseProgram(program);
272 
273     GLint positionLocation = glGetAttribLocation(program, "a_position");
274     glEnableVertexAttribArray(positionLocation);
275     glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2, 0);
276 
277     GLint colorLocation = glGetAttribLocation(program, "a_color");
278     glEnableVertexAttribArray(colorLocation);
279     glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2,
280                           reinterpret_cast<const void *>(sizeof(angle::Vector4)));
281 
282     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
283     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
284 
285     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
286                    reinterpret_cast<const void *>(sizeof(GLshort) * 6));
287     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
288 
289     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
290     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
291 
292     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
293                    reinterpret_cast<const void *>(sizeof(GLshort) * 6));
294     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
295 }
296 
297 // Covers drawing with a very large vertex range which overflows GLsizei. http://crbug.com/842028
TEST_P(RobustBufferAccessBehaviorTest,VeryLargeVertexCountWithDynamicVertexData)298 TEST_P(RobustBufferAccessBehaviorTest, VeryLargeVertexCountWithDynamicVertexData)
299 {
300     ANGLE_SKIP_TEST_IF(!initExtension());
301     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_element_index_uint"));
302 
303     constexpr GLsizei kIndexCount           = 32;
304     std::array<GLuint, kIndexCount> indices = {{}};
305     for (GLsizei index = 0; index < kIndexCount; ++index)
306     {
307         indices[index] = ((std::numeric_limits<GLuint>::max() - 2) / kIndexCount) * index;
308     }
309 
310     GLBuffer indexBuffer;
311     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
312     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(),
313                  GL_STATIC_DRAW);
314 
315     std::array<GLfloat, 256> vertexData = {{}};
316 
317     GLBuffer vertexBuffer;
318     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
319     glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(GLfloat), vertexData.data(),
320                  GL_DYNAMIC_DRAW);
321 
322     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
323     glUseProgram(program);
324 
325     GLint attribLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
326     ASSERT_NE(-1, attribLoc);
327 
328     glVertexAttribPointer(attribLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
329     glEnableVertexAttribArray(attribLoc);
330     ASSERT_GL_NO_ERROR();
331 
332     glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_INT, nullptr);
333 
334     // This may or may not generate an error, but it should not crash.
335 }
336 
337 // Test that robust access works even if there's no data uploaded to the vertex buffer at all.
TEST_P(RobustBufferAccessBehaviorTest,NoBufferData)338 TEST_P(RobustBufferAccessBehaviorTest, NoBufferData)
339 {
340     ANGLE_SKIP_TEST_IF(!initExtension());
341     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
342     glUseProgram(program);
343 
344     glEnableVertexAttribArray(0);
345     GLBuffer buf;
346     glBindBuffer(GL_ARRAY_BUFFER, buf);
347 
348     glVertexAttribPointer(0, 1, GL_FLOAT, false, 0, nullptr);
349     ASSERT_GL_NO_ERROR();
350 
351     std::array<GLubyte, 1u> indices = {0};
352     glDrawElements(GL_POINTS, indices.size(), GL_UNSIGNED_BYTE, indices.data());
353     ASSERT_GL_NO_ERROR();
354 }
355 
356 constexpr char kWebGLVS[] = R"(attribute vec2 position;
357 attribute vec4 aOne;
358 attribute vec4 aTwo;
359 varying vec4 v;
360 uniform vec2 comparison;
361 
362 bool isRobust(vec4 value) {
363     // The valid buffer range is filled with this value.
364     if (value.xy == comparison)
365         return true;
366     // Checking the w value is a bit complex.
367     return (value.xyz == vec3(0, 0, 0));
368 }
369 
370 void main() {
371     gl_Position = vec4(position, 0, 1);
372     if (isRobust(aOne) && isRobust(aTwo)) {
373         v = vec4(0, 1, 0, 1);
374     } else {
375         v = vec4(1, 0, 0, 1);
376     }
377 })";
378 
379 constexpr char kWebGLFS[] = R"(precision mediump float;
380 varying vec4 v;
381 void main() {
382     gl_FragColor = v;
383 })";
384 
385 // Test buffer with interleaved (3+2) float vectors. Adapted from WebGL test
386 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,InterleavedAttributes)387 TEST_P(RobustBufferAccessBehaviorTest, InterleavedAttributes)
388 {
389     ANGLE_SKIP_TEST_IF(!initExtension());
390 
391     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
392     glUseProgram(program);
393 
394     constexpr GLint kPosLoc = 0;
395     constexpr GLint kOneLoc = 1;
396     constexpr GLint kTwoLoc = 2;
397 
398     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
399     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
400     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
401 
402     // Create a buffer of 200 valid sets of quad lists.
403     constexpr size_t kNumQuads = 200;
404     using QuadVerts            = std::array<Vector3, 6>;
405     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
406 
407     GLBuffer positionBuf;
408     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
409     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
410     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
411     glEnableVertexAttribArray(kPosLoc);
412 
413     constexpr GLfloat kDefaultFloat = 0.2f;
414     std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
415 
416     GLBuffer vbo;
417     glBindBuffer(GL_ARRAY_BUFFER, vbo);
418 
419     // enough for 9 vertices, so 3 triangles
420     glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_STATIC_DRAW);
421 
422     // bind first 3 elements, with a stride of 5 float elements
423     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
424     // bind 2 elements, starting after the first 3; same stride of 5 float elements
425     glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
426                           reinterpret_cast<const GLvoid *>(3 * 4));
427 
428     glEnableVertexAttribArray(kOneLoc);
429     glEnableVertexAttribArray(kTwoLoc);
430 
431     // set test uniform
432     GLint uniLoc = glGetUniformLocation(program, "comparison");
433     ASSERT_NE(-1, uniLoc);
434     glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
435 
436     // Draw out of bounds.
437     glDrawArrays(GL_TRIANGLES, 0, 10000);
438     GLenum err = glGetError();
439     if (err == GL_NO_ERROR)
440     {
441         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
442     }
443     else
444     {
445         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
446     }
447 
448     glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
449     err = glGetError();
450     if (err == GL_NO_ERROR)
451     {
452         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
453     }
454     else
455     {
456         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
457     }
458 }
459 
460 // Tests redefining an empty buffer. Adapted from WebGL test
461 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,EmptyBuffer)462 TEST_P(RobustBufferAccessBehaviorTest, EmptyBuffer)
463 {
464     ANGLE_SKIP_TEST_IF(!initExtension());
465 
466     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
467     glUseProgram(program);
468 
469     constexpr GLint kPosLoc = 0;
470     constexpr GLint kOneLoc = 1;
471     constexpr GLint kTwoLoc = 2;
472 
473     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
474     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
475     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
476 
477     // Create a buffer of 200 valid sets of quad lists.
478     constexpr size_t kNumQuads = 200;
479     using QuadVerts            = std::array<Vector3, 6>;
480     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
481 
482     GLBuffer positionBuf;
483     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
484     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
485     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
486     glEnableVertexAttribArray(kPosLoc);
487 
488     // set test uniform
489     GLint uniLoc = glGetUniformLocation(program, "comparison");
490     ASSERT_NE(-1, uniLoc);
491     glUniform2f(uniLoc, 0, 0);
492 
493     // Define empty buffer.
494     GLBuffer buffer;
495     glBindBuffer(GL_ARRAY_BUFFER, buffer);
496     glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
497     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
498     glEnableVertexAttribArray(kOneLoc);
499     glDrawArrays(GL_TRIANGLES, 0, 3);
500     GLenum err = glGetError();
501     if (err == GL_NO_ERROR)
502     {
503         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
504     }
505     else
506     {
507         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
508     }
509 
510     // Redefine buffer with 3 float vectors.
511     constexpr GLfloat kFloats[] = {0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0};
512     glBufferData(GL_ARRAY_BUFFER, sizeof(kFloats), kFloats, GL_STATIC_DRAW);
513     glDrawArrays(GL_TRIANGLES, 0, 3);
514     ASSERT_GL_NO_ERROR();
515 }
516 
517 // Tests robust buffer access with dynamic buffer usage.
TEST_P(RobustBufferAccessBehaviorTest,DynamicBuffer)518 TEST_P(RobustBufferAccessBehaviorTest, DynamicBuffer)
519 {
520     ANGLE_SKIP_TEST_IF(!initExtension());
521 
522     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
523     glUseProgram(program);
524 
525     constexpr GLint kPosLoc = 0;
526     constexpr GLint kOneLoc = 1;
527     constexpr GLint kTwoLoc = 2;
528 
529     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
530     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
531     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
532 
533     // Create a buffer of 200 valid sets of quad lists.
534     constexpr size_t kNumQuads = 200;
535     using QuadVerts            = std::array<Vector3, 6>;
536     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
537 
538     GLBuffer positionBuf;
539     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
540     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
541     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
542     glEnableVertexAttribArray(kPosLoc);
543 
544     constexpr GLfloat kDefaultFloat = 0.2f;
545     std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
546 
547     GLBuffer vbo;
548     glBindBuffer(GL_ARRAY_BUFFER, vbo);
549 
550     // enough for 9 vertices, so 3 triangles
551     glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_DYNAMIC_DRAW);
552 
553     // bind first 3 elements, with a stride of 5 float elements
554     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
555     // bind 2 elements, starting after the first 3; same stride of 5 float elements
556     glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
557                           reinterpret_cast<const GLvoid *>(3 * 4));
558 
559     glEnableVertexAttribArray(kOneLoc);
560     glEnableVertexAttribArray(kTwoLoc);
561 
562     // set test uniform
563     GLint uniLoc = glGetUniformLocation(program, "comparison");
564     ASSERT_NE(-1, uniLoc);
565     glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
566 
567     // Draw out of bounds.
568     glDrawArrays(GL_TRIANGLES, 0, 10000);
569     GLenum err = glGetError();
570     if (err == GL_NO_ERROR)
571     {
572         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
573     }
574     else
575     {
576         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
577     }
578 
579     glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
580     err = glGetError();
581     if (err == GL_NO_ERROR)
582     {
583         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
584     }
585     else
586     {
587         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
588     }
589 }
590 
591 // Tests out of bounds read by divisor emulation due to a user-provided offset.
592 // Adapted from https://crbug.com/1285885.
TEST_P(RobustBufferAccessBehaviorTest,IndexOutOfBounds)593 TEST_P(RobustBufferAccessBehaviorTest, IndexOutOfBounds)
594 {
595     ANGLE_SKIP_TEST_IF(!initExtension());
596 
597     constexpr char kVS[] = R"(precision highp float;
598 attribute vec4 a_position;
599 void main(void) {
600    gl_Position = a_position;
601 })";
602 
603     constexpr char kFS[] = R"(precision highp float;
604 uniform sampler2D oTexture;
605 uniform float oColor[3];
606 void main(void) {
607    gl_FragData[0] = texture2DProj(oTexture, vec3(0.1,0.1,0.1));
608 })";
609 
610     GLfloat singleFloat = 1.0f;
611 
612     GLBuffer buf;
613     glBindBuffer(GL_ARRAY_BUFFER, buf);
614     glBufferData(GL_ARRAY_BUFFER, 4, &singleFloat, GL_STATIC_DRAW);
615 
616     ANGLE_GL_PROGRAM(program, kVS, kFS);
617     glBindAttribLocation(program, 0, "a_position");
618     glLinkProgram(program);
619     ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
620 
621     glEnableVertexAttribArray(0);
622 
623     // Trying to exceed renderer->getMaxVertexAttribDivisor()
624     GLuint constexpr kDivisor = 4096;
625     glVertexAttribDivisor(0, kDivisor);
626 
627     size_t outOfBoundsOffset = 0x50000000;
628     glVertexAttribPointer(0, 1, GL_FLOAT, false, 8, reinterpret_cast<void *>(outOfBoundsOffset));
629 
630     glUseProgram(program);
631 
632     glDrawArrays(GL_TRIANGLES, 0, 32);
633 
634     // No assertions, just checking for crashes.
635 }
636 
637 // Similar to the test above but index is first within bounds then goes out of bounds.
TEST_P(RobustBufferAccessBehaviorTest,IndexGoingOutOfBounds)638 TEST_P(RobustBufferAccessBehaviorTest, IndexGoingOutOfBounds)
639 {
640     ANGLE_SKIP_TEST_IF(!initExtension());
641 
642     constexpr char kVS[] = R"(precision highp float;
643 attribute vec4 a_position;
644 void main(void) {
645    gl_Position = a_position;
646 })";
647 
648     constexpr char kFS[] = R"(precision highp float;
649 uniform sampler2D oTexture;
650 uniform float oColor[3];
651 void main(void) {
652    gl_FragData[0] = texture2DProj(oTexture, vec3(0.1,0.1,0.1));
653 })";
654 
655     GLBuffer buf;
656     glBindBuffer(GL_ARRAY_BUFFER, buf);
657     std::array<GLfloat, 2> buffer = {{0.2f, 0.2f}};
658     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * buffer.size(), buffer.data(), GL_STATIC_DRAW);
659 
660     ANGLE_GL_PROGRAM(program, kVS, kFS);
661     glBindAttribLocation(program, 0, "a_position");
662     glLinkProgram(program);
663     ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
664 
665     glEnableVertexAttribArray(0);
666 
667     // Trying to exceed renderer->getMaxVertexAttribDivisor()
668     GLuint constexpr kDivisor = 4096;
669     glVertexAttribDivisor(0, kDivisor);
670 
671     // 6 bytes remaining in the buffer from offset so only a single vertex can be read
672     glVertexAttribPointer(0, 1, GL_FLOAT, false, 8, reinterpret_cast<void *>(2));
673 
674     glUseProgram(program);
675 
676     // Each vertex is read `kDivisor` times so the last read goes out of bounds
677     GLsizei instanceCount = kDivisor + 1;
678     glDrawArraysInstanced(GL_TRIANGLES, 0, 32, instanceCount);
679 
680     // No assertions, just checking for crashes.
681 }
682 
683 // Draw out-of-bounds beginning with the start offset passed in.
684 // Ensure that drawArrays flags either no error or INVALID_OPERATION. In the case of
685 // INVALID_OPERATION, no canvas pixels can be touched.  In the case of NO_ERROR, all written values
686 // must either be the zero vertex or a value in the vertex buffer.  See vsCheckOutOfBounds shader.
DrawAndVerifyOutOfBoundsArrays(int first,int count)687 void DrawAndVerifyOutOfBoundsArrays(int first, int count)
688 {
689     glClearColor(0.0, 0.0, 1.0, 1.0);  // Start with blue to indicate no pixels touched.
690     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
691 
692     glDrawArrays(GL_TRIANGLES, first, count);
693     GLenum error = glGetError();
694     if (error == GL_INVALID_OPERATION)
695     {
696         // testPassed. drawArrays flagged INVALID_OPERATION, which is valid so long as all canvas
697         // pixels were not touched.
698         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
699     }
700     else
701     {
702         ASSERT_GL_NO_ERROR();
703         // testPassed. drawArrays flagged NO_ERROR, which is valid so long as all canvas pixels are
704         // green.
705         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
706     }
707 }
708 
709 // Adapted from WebGL test
710 // conformance/rendering/out-of-bounds-array-buffers.html
711 // This test verifies that out-of-bounds array buffers behave according to spec.
TEST_P(RobustBufferAccessBehaviorTest,OutOfBoundsArrayBuffers)712 TEST_P(RobustBufferAccessBehaviorTest, OutOfBoundsArrayBuffers)
713 {
714     ANGLE_SKIP_TEST_IF(!initExtension());
715 
716     constexpr char vsCheckOutOfBounds[] =
717         "precision mediump float;\n"
718         "attribute vec2 position;\n"
719         "attribute vec4 vecRandom;\n"
720         "varying vec4 v_color;\n"
721         "\n"
722         "// Per the spec, each component can either contain existing contents\n"
723         "// of the buffer or 0.\n"
724         "bool testFloatComponent(float component) {\n"
725         "   return (component == 0.2 || component == 0.0);\n"
726         "}\n"
727         ""  // The last component is additionally allowed to be 1.0.\n"
728         "bool testLastFloatComponent(float component) {\n"
729         "   return testFloatComponent(component) || component == 1.0;\n"
730         "}\n"
731         "\n"
732         "void main() {\n"
733         "   if (testFloatComponent(vecRandom.x) &&\n"
734         "       testFloatComponent(vecRandom.y) &&\n"
735         "       testFloatComponent(vecRandom.z) &&\n"
736         "       testLastFloatComponent(vecRandom.w)) {\n"
737         "           v_color = vec4(0.0, 1.0, 0.0, 1.0); // green -- We're good\n"
738         "       } else {\n"
739         "           v_color = vec4(1.0, 0.0, 0.0, 1.0); // red -- Unexpected value\n"
740         "       }\n"
741         "   gl_Position = vec4(position, 0.0, 1.0);\n"
742         "}\n";
743 
744     constexpr char simpleVertexColorFragmentShader[] =
745         "precision mediump float;\n"
746         "varying vec4 v_color;\n"
747         "void main() {\n"
748         "    gl_FragColor = v_color;\n"
749         "}";
750 
751     // Setup the verification program.
752     ANGLE_GL_PROGRAM(program, vsCheckOutOfBounds, simpleVertexColorFragmentShader);
753     glUseProgram(program);
754 
755     GLint kPosLoc = glGetAttribLocation(program, "position");
756     ASSERT_NE(kPosLoc, -1);
757     GLint kRandomLoc = glGetAttribLocation(program, "vecRandom");
758     ASSERT_NE(kRandomLoc, -1);
759 
760     // Create a buffer of 200 valid sets of quad lists.
761     constexpr size_t numberOfQuads = 200;
762     // Create a vertex buffer with 200 properly formed triangle quads. These quads will cover the
763     // canvas texture such that every single pixel is touched by the fragment shader.
764     GLBuffer glQuadBuffer;
765     glBindBuffer(GL_ARRAY_BUFFER, glQuadBuffer);
766     std::array<float, numberOfQuads * 2 * 6> quadPositions;
767     for (unsigned int i = 0; i < quadPositions.size(); i += 2 * 6)
768     {
769         quadPositions[i + 0]  = -1.0;  // upper left
770         quadPositions[i + 1]  = 1.0;
771         quadPositions[i + 2]  = 1.0;  // upper right
772         quadPositions[i + 3]  = 1.0;
773         quadPositions[i + 4]  = -1.0;  // lower left
774         quadPositions[i + 5]  = -1.0;
775         quadPositions[i + 6]  = 1.0;  // upper right
776         quadPositions[i + 7]  = 1.0;
777         quadPositions[i + 8]  = 1.0;  // lower right
778         quadPositions[i + 9]  = -1.0;
779         quadPositions[i + 10] = -1.0;  // lower left
780         quadPositions[i + 11] = -1.0;
781     }
782     glBufferData(GL_ARRAY_BUFFER, quadPositions.size() * sizeof(float), quadPositions.data(),
783                  GL_STATIC_DRAW);
784     glEnableVertexAttribArray(kPosLoc);
785     glVertexAttribPointer(kPosLoc, 2, GL_FLOAT, false, 0, 0);
786 
787     // Create a small vertex buffer with determined-ahead-of-time "random" values (0.2). This buffer
788     // will be the one read past the end.
789     GLBuffer glVertexBuffer;
790     glBindBuffer(GL_ARRAY_BUFFER, glVertexBuffer);
791     std::array<float, 6> vertexData = {0.2, 0.2, 0.2, 0.2, 0.2, 0.2};
792     glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(),
793                  GL_STATIC_DRAW);
794     glEnableVertexAttribArray(kRandomLoc);
795     glVertexAttribPointer(kRandomLoc, 4, GL_FLOAT, false, 0, 0);
796 
797     // Test -- Draw off the end of the vertex buffer near the beginning of the out of bounds area.
798     DrawAndVerifyOutOfBoundsArrays(/*first*/ 6, /*count*/ 6);
799 
800     // Test -- Draw off the end of the vertex buffer near the end of the out of bounds area.
801     DrawAndVerifyOutOfBoundsArrays(/*first*/ (numberOfQuads - 1) * 6, /*count*/ 6);
802 }
803 
804 // Regression test for glBufferData with slightly increased size. Implementation may decided to
805 // reuse the buffer storage if underline storage is big enough (due to alignment, implementation may
806 // allocate more storage than data size.) This tests ensure it works correctly when this reuse
807 // happens.
TEST_P(RobustBufferAccessBehaviorTest,BufferDataWithIncreasedSize)808 TEST_P(RobustBufferAccessBehaviorTest, BufferDataWithIncreasedSize)
809 {
810     ANGLE_SKIP_TEST_IF(!initExtension());
811 
812     ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Green());
813 
814     // Clear to red and draw one triangle on the bottom left with green. The right top half should
815     // be red.
816     glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
817     glClear(GL_COLOR_BUFFER_BIT);
818     std::array<float, 2 * 3> quadVertices = {-1, 1, -1, -1, 1, -1};
819     constexpr size_t kBufferSize          = sizeof(quadVertices[0]) * quadVertices.size();
820     GLBuffer vertexBuffer;
821     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
822     glBufferData(GL_ARRAY_BUFFER, kBufferSize, quadVertices.data(), GL_STATIC_DRAW);
823     glUseProgram(drawGreen);
824     const GLint positionLocation = glGetAttribLocation(drawGreen, essl1_shaders::PositionAttrib());
825     ASSERT_NE(-1, positionLocation);
826     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
827     glEnableVertexAttribArray(positionLocation);
828     glDrawArrays(GL_TRIANGLES, 0, 3);
829     EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
830     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
831     EXPECT_GL_NO_ERROR();
832 
833     // Clear to blue and call glBufferData with two triangles and draw the entire window with green.
834     // Both bottom left and top right should be green.
835     glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
836     glClear(GL_COLOR_BUFFER_BIT);
837     std::array<float, 2 * 3 * 2> twoQuadVertices = {-1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1};
838     glBufferData(GL_ARRAY_BUFFER, kBufferSize * 2, twoQuadVertices.data(), GL_STATIC_DRAW);
839     glUseProgram(drawGreen);
840     glDrawArrays(GL_TRIANGLES, 0, 6);
841     EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
842     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
843     EXPECT_GL_NO_ERROR();
844 }
845 
846 // Similar to BufferDataWithIncreasedSize. But this time the buffer is bound to two VAOs. The change
847 // in the buffer should be picked up by both VAOs.
TEST_P(RobustBufferAccessBehaviorTest,BufferDataWithIncreasedSizeAndUseWithVAOs)848 TEST_P(RobustBufferAccessBehaviorTest, BufferDataWithIncreasedSizeAndUseWithVAOs)
849 {
850     ANGLE_SKIP_TEST_IF(!initExtension());
851 
852     ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Green());
853 
854     // Clear to red and draw one triangle with VAO1 on the bottom left with green. The right top
855     // half should be red.
856     glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
857     glClear(GL_COLOR_BUFFER_BIT);
858     std::array<float, 2 * 3> quadVertices = {-1, 1, -1, -1, 1, -1};
859     constexpr size_t kBufferSize          = sizeof(quadVertices[0]) * quadVertices.size();
860     GLBuffer vertexBuffer;
861     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
862     glBufferData(GL_ARRAY_BUFFER, kBufferSize, quadVertices.data(), GL_STATIC_DRAW);
863     glUseProgram(drawGreen);
864     const GLint positionLocation = glGetAttribLocation(drawGreen, essl1_shaders::PositionAttrib());
865     ASSERT_NE(-1, positionLocation);
866     GLVertexArray vao1;
867     glBindVertexArray(vao1);
868     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
869     glEnableVertexAttribArray(positionLocation);
870     glDrawArrays(GL_TRIANGLES, 0, 3);
871     EXPECT_PIXEL_COLOR_EQ(2, 2, GLColor::green);
872     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
873     EXPECT_GL_NO_ERROR();
874 
875     // Now use the same buffer on VAO2
876     GLVertexArray vao2;
877     glBindVertexArray(vao2);
878     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
879     glEnableVertexAttribArray(positionLocation);
880     glDrawArrays(GL_TRIANGLES, 0, 3);
881     EXPECT_PIXEL_COLOR_EQ(2, 2, GLColor::green);
882     // Clear to blue and call glBufferData with two triangles and draw the entire window with green.
883     // Both bottom left and top right should be green.
884     glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
885     glClear(GL_COLOR_BUFFER_BIT);
886     std::array<float, 2 * 3 * 2> twoQuadVertices = {-1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1};
887     glBufferData(GL_ARRAY_BUFFER, kBufferSize * 2, twoQuadVertices.data(), GL_STATIC_DRAW);
888     glUseProgram(drawGreen);
889     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
890     glEnableVertexAttribArray(positionLocation);
891     glDrawArrays(GL_TRIANGLES, 0, 6);
892     EXPECT_PIXEL_COLOR_EQ(2, 2, GLColor::green);
893     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
894     EXPECT_GL_NO_ERROR();
895 
896     // Buffer's change should be piked by VAO1 as well. If not, then we should get validation error.
897     glBindVertexArray(vao1);
898     glDrawArrays(GL_TRIANGLES, 0, 3);
899     EXPECT_PIXEL_COLOR_EQ(2, 2, GLColor::green);
900     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
901     EXPECT_GL_NO_ERROR();
902 }
903 
904 // Prepare an element array buffer that indexes out-of-bounds beginning with the start index passed
905 // in. Ensure that drawElements flags either no error or INVALID_OPERATION. In the case of
906 // INVALID_OPERATION, no canvas pixels can be touched.  In the case of NO_ERROR, all written values
907 // must either be the zero vertex or a value in the vertex buffer.  See vsCheckOutOfBounds shader.
DrawAndVerifyOutOfBoundsIndex(int startIndex)908 void DrawAndVerifyOutOfBoundsIndex(int startIndex)
909 {
910     glClearColor(0.0, 0.0, 1.0, 1.0);  // Start with blue to indicate no pixels touched.
911     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
912 
913     // Create an element array buffer with a tri-strip that starts at startIndex and make
914     // it the active element array buffer.
915     GLBuffer glElementArrayBuffer;
916     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glElementArrayBuffer);
917     std::array<uint16_t, 4> quadIndices;
918     for (unsigned int i = 0; i < quadIndices.size(); i++)
919     {
920         quadIndices[i] = startIndex + i;
921     }
922     glBufferData(GL_ELEMENT_ARRAY_BUFFER, quadIndices.size() * sizeof(uint16_t), quadIndices.data(),
923                  GL_STATIC_DRAW);
924 
925     glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, /*offset*/ 0);
926     GLenum error = glGetError();
927     if (error == GL_INVALID_OPERATION)
928     {
929         // testPassed. drawElements flagged INVALID_OPERATION, which is valid so long as all canvas
930         // pixels were not touched.
931         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
932     }
933     else
934     {
935         ASSERT_GL_NO_ERROR();
936         // testPassed. drawElements flagged NO_ERROR, which is valid so long as all canvas pixels
937         // are green.
938         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
939     }
940 }
941 
942 // Adapted from WebGL test
943 // conformance/rendering/out-of-bounds-index-buffers.html
944 // This test verifies that out-of-bounds index buffers behave according to spec.
TEST_P(RobustBufferAccessBehaviorTest,OutOfBoundsIndexBuffers)945 TEST_P(RobustBufferAccessBehaviorTest, OutOfBoundsIndexBuffers)
946 {
947     ANGLE_SKIP_TEST_IF(!initExtension());
948 
949     constexpr char vsCheckOutOfBounds[] =
950         "precision mediump float;\n"
951         "attribute vec2 position;\n"
952         "attribute vec4 vecRandom;\n"
953         "varying vec4 v_color;\n"
954         "\n"
955         "// Per the spec, each component can either contain existing contents\n"
956         "// of the buffer or 0.\n"
957         "bool testFloatComponent(float component) {\n"
958         "   return (component == 0.2 || component == 0.0);\n"
959         "}\n"
960         ""  // The last component is additionally allowed to be 1.0.\n"
961         "bool testLastFloatComponent(float component) {\n"
962         "   return testFloatComponent(component) || component == 1.0;\n"
963         "}\n"
964         "\n"
965         "void main() {\n"
966         "   if (testFloatComponent(vecRandom.x) &&\n"
967         "       testFloatComponent(vecRandom.y) &&\n"
968         "       testFloatComponent(vecRandom.z) &&\n"
969         "       testLastFloatComponent(vecRandom.w)) {\n"
970         "           v_color = vec4(0.0, 1.0, 0.0, 1.0); // green -- We're good\n"
971         "       } else {\n"
972         "           v_color = vec4(1.0, 0.0, 0.0, 1.0); // red -- Unexpected value\n"
973         "       }\n"
974         "   gl_Position = vec4(position, 0.0, 1.0);\n"
975         "}\n";
976 
977     constexpr char simpleVertexColorFragmentShader[] =
978         "precision mediump float;\n"
979         "varying vec4 v_color;\n"
980         "void main() {\n"
981         "    gl_FragColor = v_color;\n"
982         "}";
983 
984     // Setup the verification program.
985     ANGLE_GL_PROGRAM(program, vsCheckOutOfBounds, simpleVertexColorFragmentShader);
986     glUseProgram(program);
987 
988     GLint kPosLoc = glGetAttribLocation(program, "position");
989     ASSERT_NE(kPosLoc, -1);
990     GLint kRandomLoc = glGetAttribLocation(program, "vecRandom");
991     ASSERT_NE(kRandomLoc, -1);
992 
993     // Create a buffer of 200 valid sets of quad lists.
994     constexpr size_t numberOfQuads = 200;
995     // Create a vertex buffer with 200 properly formed tri-strip quads. These quads will cover the
996     // canvas texture such that every single pixel is touched by the fragment shader.
997     GLBuffer glQuadBuffer;
998     glBindBuffer(GL_ARRAY_BUFFER, glQuadBuffer);
999     std::array<float, numberOfQuads * 2 * 4> quadPositions;
1000     for (unsigned int i = 0; i < quadPositions.size(); i += 2 * 4)
1001     {
1002         quadPositions[i + 0] = -1.0;  // upper left
1003         quadPositions[i + 1] = 1.0;
1004         quadPositions[i + 2] = 1.0;  // upper right
1005         quadPositions[i + 3] = 1.0;
1006         quadPositions[i + 4] = -1.0;  // lower left
1007         quadPositions[i + 5] = -1.0;
1008         quadPositions[i + 6] = 1.0;  // lower right
1009         quadPositions[i + 7] = -1.0;
1010     }
1011     glBufferData(GL_ARRAY_BUFFER, quadPositions.size() * sizeof(float), quadPositions.data(),
1012                  GL_STATIC_DRAW);
1013     glEnableVertexAttribArray(kPosLoc);
1014     glVertexAttribPointer(kPosLoc, 2, GL_FLOAT, false, 0, 0);
1015 
1016     // Create a small vertex buffer with determined-ahead-of-time "random" values (0.2). This buffer
1017     // will be the one indexed off the end.
1018     GLBuffer glVertexBuffer;
1019     glBindBuffer(GL_ARRAY_BUFFER, glVertexBuffer);
1020     std::array<float, 4> vertexData = {0.2, 0.2, 0.2, 0.2};
1021     glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(),
1022                  GL_STATIC_DRAW);
1023     glEnableVertexAttribArray(kRandomLoc);
1024     glVertexAttribPointer(kRandomLoc, 4, GL_FLOAT, false, 0, 0);
1025 
1026     // Test -- Index off the end of the vertex buffer near the beginning of the out of bounds area.
1027     DrawAndVerifyOutOfBoundsIndex(/*StartIndex*/ 4);
1028 
1029     // Test -- Index off the end of the vertex buffer near the end of the out of bounds area.
1030     DrawAndVerifyOutOfBoundsIndex(/*StartIndex*/ numberOfQuads - 4);
1031 }
1032 
1033 ANGLE_INSTANTIATE_TEST(RobustBufferAccessBehaviorTest,
1034                        WithNoFixture(ES3_VULKAN()),
1035                        WithNoFixture(ES3_OPENGL()),
1036                        WithNoFixture(ES3_OPENGLES()),
1037                        WithNoFixture(ES3_D3D11()),
1038                        WithNoFixture(ES3_METAL()));
1039 
1040 }  // namespace
1041