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