• 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 
47         Library *driverLib = ANGLETestEnvironment::GetDriverLibrary(GLESDriverType::AngleEGL);
48 
49         mEGLWindow = EGLWindow::New(GetParam().majorVersion, GetParam().minorVersion);
50         if (!mEGLWindow->initializeDisplay(mOSWindow, driverLib, GLESDriverType::AngleEGL,
51                                            GetParam().eglParameters))
52         {
53             return false;
54         }
55 
56         EGLDisplay display = mEGLWindow->getDisplay();
57         if (!IsEGLDisplayExtensionEnabled(display, "EGL_EXT_create_context_robustness"))
58         {
59             return false;
60         }
61 
62         ConfigParameters configParams;
63         configParams.redBits      = 8;
64         configParams.greenBits    = 8;
65         configParams.blueBits     = 8;
66         configParams.alphaBits    = 8;
67         configParams.robustAccess = true;
68 
69         if (mEGLWindow->initializeSurface(mOSWindow, driverLib, configParams) !=
70             GLWindowResult::NoError)
71         {
72             return false;
73         }
74 
75         if (!mEGLWindow->initializeContext())
76         {
77             return false;
78         }
79 
80         if (!mEGLWindow->makeCurrent())
81         {
82             return false;
83         }
84 
85         if (!IsGLExtensionEnabled("GL_KHR_robust_buffer_access_behavior"))
86         {
87             return false;
88         }
89         return true;
90     }
91 
initBasicProgram()92     void initBasicProgram()
93     {
94         constexpr char kVS[] =
95             "precision mediump float;\n"
96             "attribute vec4 position;\n"
97             "attribute vec4 vecRandom;\n"
98             "varying vec4 v_color;\n"
99             "bool testFloatComponent(float component) {\n"
100             "    return (component == 0.2 || component == 0.0);\n"
101             "}\n"
102             "bool testLastFloatComponent(float component) {\n"
103             "    return testFloatComponent(component) || component == 1.0;\n"
104             "}\n"
105             "void main() {\n"
106             "    if (testFloatComponent(vecRandom.x) &&\n"
107             "        testFloatComponent(vecRandom.y) &&\n"
108             "        testFloatComponent(vecRandom.z) &&\n"
109             "        testLastFloatComponent(vecRandom.w)) {\n"
110             "        v_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
111             "    } else {\n"
112             "        v_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
113             "    }\n"
114             "    gl_Position = position;\n"
115             "}\n";
116 
117         constexpr char kFS[] =
118             "precision mediump float;\n"
119             "varying vec4 v_color;\n"
120             "void main() {\n"
121             "    gl_FragColor = v_color;\n"
122             "}\n";
123 
124         mProgram = CompileProgram(kVS, kFS);
125         ASSERT_NE(0u, mProgram);
126 
127         mTestAttrib = glGetAttribLocation(mProgram, "vecRandom");
128         ASSERT_NE(-1, mTestAttrib);
129 
130         glUseProgram(mProgram);
131     }
132 
runIndexOutOfRangeTests(GLenum drawType)133     void runIndexOutOfRangeTests(GLenum drawType)
134     {
135         if (mProgram == 0)
136         {
137             initBasicProgram();
138         }
139 
140         GLBuffer bufferIncomplete;
141         glBindBuffer(GL_ARRAY_BUFFER, bufferIncomplete);
142         std::array<GLfloat, 12> randomData = {
143             {0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f}};
144         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * randomData.size(), randomData.data(),
145                      drawType);
146 
147         glEnableVertexAttribArray(mTestAttrib);
148         glVertexAttribPointer(mTestAttrib, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
149 
150         glClearColor(0.0, 0.0, 1.0, 1.0);
151         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
152 
153         drawIndexedQuad(mProgram, "position", 0.5f);
154 
155         int width     = getWindowWidth();
156         int height    = getWindowHeight();
157         GLenum result = glGetError();
158         // For D3D dynamic draw, we still return invalid operation. Once we force the index buffer
159         // to clamp any out of range indices instead of invalid operation, this part can be removed.
160         // We can always get GL_NO_ERROR.
161         if (result == GL_INVALID_OPERATION)
162         {
163             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::blue);
164             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::blue);
165             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::blue);
166             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::blue);
167         }
168         else
169         {
170             EXPECT_GLENUM_EQ(GL_NO_ERROR, result);
171             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::green);
172             EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::green);
173             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::green);
174             EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::green);
175         }
176     }
177 
178     OSWindow *mOSWindow   = nullptr;
179     EGLWindow *mEGLWindow = nullptr;
180     GLuint mProgram       = 0;
181     GLint mTestAttrib     = 0;
182 };
183 
184 // Test that static draw with out-of-bounds reads will not read outside of the data store of the
185 // buffer object and will not result in GL interruption or termination when
186 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithStaticDraw)187 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithStaticDraw)
188 {
189     ANGLE_SKIP_TEST_IF(!initExtension());
190 
191     runIndexOutOfRangeTests(GL_STATIC_DRAW);
192 }
193 
194 // Test that dynamic draw with out-of-bounds reads will not read outside of the data store of the
195 // buffer object and will not result in GL interruption or termination when
196 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithDynamicDraw)197 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithDynamicDraw)
198 {
199     ANGLE_SKIP_TEST_IF(!initExtension());
200 
201     runIndexOutOfRangeTests(GL_DYNAMIC_DRAW);
202 }
203 
204 // Test that vertex buffers are rebound with the correct offsets in subsequent calls in the D3D11
205 // backend.  http://crbug.com/837002
TEST_P(RobustBufferAccessBehaviorTest,D3D11StateSynchronizationOrderBug)206 TEST_P(RobustBufferAccessBehaviorTest, D3D11StateSynchronizationOrderBug)
207 {
208     ANGLE_SKIP_TEST_IF(!initExtension());
209 
210     glDisable(GL_DEPTH_TEST);
211 
212     // 2 quads, the first one red, the second one green
213     const std::array<angle::Vector4, 16> vertices{
214         angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v0
215         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c0
216         angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v1
217         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c1
218         angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v2
219         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c2
220         angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v3
221         angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c3
222 
223         angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v4
224         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c4
225         angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v5
226         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c5
227         angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v6
228         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c6
229         angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v7
230         angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c7
231     };
232 
233     GLBuffer vb;
234     glBindBuffer(GL_ARRAY_BUFFER, vb);
235     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
236 
237     const std::array<GLushort, 12> indicies{
238         0, 1, 2, 0, 2, 3,  // quad0
239         4, 5, 6, 4, 6, 7,  // quad1
240     };
241 
242     GLBuffer ib;
243     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
244     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies.data(), GL_STATIC_DRAW);
245 
246     constexpr char kVS[] = R"(
247 precision highp float;
248 attribute vec4 a_position;
249 attribute vec4 a_color;
250 
251 varying vec4 v_color;
252 
253 void main()
254 {
255     gl_Position = a_position;
256     v_color = a_color;
257 })";
258 
259     constexpr char kFS[] = R"(
260 precision highp float;
261 varying vec4 v_color;
262 
263 void main()
264 {
265     gl_FragColor = v_color;
266 })";
267 
268     ANGLE_GL_PROGRAM(program, kVS, kFS);
269     glUseProgram(program);
270 
271     GLint positionLocation = glGetAttribLocation(program, "a_position");
272     glEnableVertexAttribArray(positionLocation);
273     glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2, 0);
274 
275     GLint colorLocation = glGetAttribLocation(program, "a_color");
276     glEnableVertexAttribArray(colorLocation);
277     glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2,
278                           reinterpret_cast<const void *>(sizeof(angle::Vector4)));
279 
280     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
281     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
282 
283     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
284                    reinterpret_cast<const void *>(sizeof(GLshort) * 6));
285     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
286 
287     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
288     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
289 
290     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
291                    reinterpret_cast<const void *>(sizeof(GLshort) * 6));
292     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
293 }
294 
295 // Covers drawing with a very large vertex range which overflows GLsizei. http://crbug.com/842028
TEST_P(RobustBufferAccessBehaviorTest,VeryLargeVertexCountWithDynamicVertexData)296 TEST_P(RobustBufferAccessBehaviorTest, VeryLargeVertexCountWithDynamicVertexData)
297 {
298     ANGLE_SKIP_TEST_IF(!initExtension());
299     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_element_index_uint"));
300 
301     constexpr GLsizei kIndexCount           = 32;
302     std::array<GLuint, kIndexCount> indices = {{}};
303     for (GLsizei index = 0; index < kIndexCount; ++index)
304     {
305         indices[index] = ((std::numeric_limits<GLuint>::max() - 2) / kIndexCount) * index;
306     }
307 
308     GLBuffer indexBuffer;
309     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
310     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(),
311                  GL_STATIC_DRAW);
312 
313     std::array<GLfloat, 256> vertexData = {{}};
314 
315     GLBuffer vertexBuffer;
316     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
317     glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(GLfloat), vertexData.data(),
318                  GL_DYNAMIC_DRAW);
319 
320     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
321     glUseProgram(program);
322 
323     GLint attribLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
324     ASSERT_NE(-1, attribLoc);
325 
326     glVertexAttribPointer(attribLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
327     glEnableVertexAttribArray(attribLoc);
328     ASSERT_GL_NO_ERROR();
329 
330     glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_INT, nullptr);
331 
332     // This may or may not generate an error, but it should not crash.
333 }
334 
335 // Test that robust access works even if there's no data uploaded to the vertex buffer at all.
TEST_P(RobustBufferAccessBehaviorTest,NoBufferData)336 TEST_P(RobustBufferAccessBehaviorTest, NoBufferData)
337 {
338     ANGLE_SKIP_TEST_IF(!initExtension());
339     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
340     glUseProgram(program);
341 
342     glEnableVertexAttribArray(0);
343     GLBuffer buf;
344     glBindBuffer(GL_ARRAY_BUFFER, buf);
345 
346     glVertexAttribPointer(0, 1, GL_FLOAT, false, 0, nullptr);
347     ASSERT_GL_NO_ERROR();
348 
349     std::array<GLubyte, 1u> indices = {0};
350     glDrawElements(GL_POINTS, indices.size(), GL_UNSIGNED_BYTE, indices.data());
351     ASSERT_GL_NO_ERROR();
352 }
353 
354 constexpr char kWebGLVS[] = R"(attribute vec2 position;
355 attribute vec4 aOne;
356 attribute vec4 aTwo;
357 varying vec4 v;
358 uniform vec2 comparison;
359 
360 bool isRobust(vec4 value) {
361     // The valid buffer range is filled with this value.
362     if (value.xy == comparison)
363         return true;
364     // Checking the w value is a bit complex.
365     return (value.xyz == vec3(0, 0, 0));
366 }
367 
368 void main() {
369     gl_Position = vec4(position, 0, 1);
370     if (isRobust(aOne) && isRobust(aTwo)) {
371         v = vec4(0, 1, 0, 1);
372     } else {
373         v = vec4(1, 0, 0, 1);
374     }
375 })";
376 
377 constexpr char kWebGLFS[] = R"(precision mediump float;
378 varying vec4 v;
379 void main() {
380     gl_FragColor = v;
381 })";
382 
383 // Test buffer with interleaved (3+2) float vectors. Adapted from WebGL test
384 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,InterleavedAttributes)385 TEST_P(RobustBufferAccessBehaviorTest, InterleavedAttributes)
386 {
387     ANGLE_SKIP_TEST_IF(!initExtension());
388 
389     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
390     glUseProgram(program);
391 
392     constexpr GLint kPosLoc = 0;
393     constexpr GLint kOneLoc = 1;
394     constexpr GLint kTwoLoc = 2;
395 
396     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
397     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
398     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
399 
400     // Create a buffer of 200 valid sets of quad lists.
401     constexpr size_t kNumQuads = 200;
402     using QuadVerts            = std::array<Vector3, 6>;
403     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
404 
405     GLBuffer positionBuf;
406     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
407     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
408     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
409     glEnableVertexAttribArray(kPosLoc);
410 
411     constexpr GLfloat kDefaultFloat = 0.2f;
412     std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
413 
414     GLBuffer vbo;
415     glBindBuffer(GL_ARRAY_BUFFER, vbo);
416 
417     // enough for 9 vertices, so 3 triangles
418     glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_STATIC_DRAW);
419 
420     // bind first 3 elements, with a stride of 5 float elements
421     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
422     // bind 2 elements, starting after the first 3; same stride of 5 float elements
423     glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
424                           reinterpret_cast<const GLvoid *>(3 * 4));
425 
426     glEnableVertexAttribArray(kOneLoc);
427     glEnableVertexAttribArray(kTwoLoc);
428 
429     // set test uniform
430     GLint uniLoc = glGetUniformLocation(program, "comparison");
431     ASSERT_NE(-1, uniLoc);
432     glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
433 
434     // Draw out of bounds.
435     glDrawArrays(GL_TRIANGLES, 0, 10000);
436     GLenum err = glGetError();
437     if (err == GL_NO_ERROR)
438     {
439         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
440     }
441     else
442     {
443         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
444     }
445 
446     glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
447     err = glGetError();
448     if (err == GL_NO_ERROR)
449     {
450         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
451     }
452     else
453     {
454         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
455     }
456 }
457 
458 // Tests redefining an empty buffer. Adapted from WebGL test
459 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,EmptyBuffer)460 TEST_P(RobustBufferAccessBehaviorTest, EmptyBuffer)
461 {
462     ANGLE_SKIP_TEST_IF(!initExtension());
463 
464     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
465     glUseProgram(program);
466 
467     constexpr GLint kPosLoc = 0;
468     constexpr GLint kOneLoc = 1;
469     constexpr GLint kTwoLoc = 2;
470 
471     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
472     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
473     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
474 
475     // Create a buffer of 200 valid sets of quad lists.
476     constexpr size_t kNumQuads = 200;
477     using QuadVerts            = std::array<Vector3, 6>;
478     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
479 
480     GLBuffer positionBuf;
481     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
482     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
483     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
484     glEnableVertexAttribArray(kPosLoc);
485 
486     // set test uniform
487     GLint uniLoc = glGetUniformLocation(program, "comparison");
488     ASSERT_NE(-1, uniLoc);
489     glUniform2f(uniLoc, 0, 0);
490 
491     // Define empty buffer.
492     GLBuffer buffer;
493     glBindBuffer(GL_ARRAY_BUFFER, buffer);
494     glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
495     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
496     glEnableVertexAttribArray(kOneLoc);
497     glDrawArrays(GL_TRIANGLES, 0, 3);
498     GLenum err = glGetError();
499     if (err == GL_NO_ERROR)
500     {
501         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
502     }
503     else
504     {
505         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
506     }
507 
508     // Redefine buffer with 3 float vectors.
509     constexpr GLfloat kFloats[] = {0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0};
510     glBufferData(GL_ARRAY_BUFFER, sizeof(kFloats), kFloats, GL_STATIC_DRAW);
511     glDrawArrays(GL_TRIANGLES, 0, 3);
512     ASSERT_GL_NO_ERROR();
513 }
514 
515 // Tests robust buffer access with dynamic buffer usage.
TEST_P(RobustBufferAccessBehaviorTest,DynamicBuffer)516 TEST_P(RobustBufferAccessBehaviorTest, DynamicBuffer)
517 {
518     ANGLE_SKIP_TEST_IF(!initExtension());
519 
520     ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
521     glUseProgram(program);
522 
523     constexpr GLint kPosLoc = 0;
524     constexpr GLint kOneLoc = 1;
525     constexpr GLint kTwoLoc = 2;
526 
527     ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
528     ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
529     ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
530 
531     // Create a buffer of 200 valid sets of quad lists.
532     constexpr size_t kNumQuads = 200;
533     using QuadVerts            = std::array<Vector3, 6>;
534     std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
535 
536     GLBuffer positionBuf;
537     glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
538     glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
539     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
540     glEnableVertexAttribArray(kPosLoc);
541 
542     constexpr GLfloat kDefaultFloat = 0.2f;
543     std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
544 
545     GLBuffer vbo;
546     glBindBuffer(GL_ARRAY_BUFFER, vbo);
547 
548     // enough for 9 vertices, so 3 triangles
549     glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_DYNAMIC_DRAW);
550 
551     // bind first 3 elements, with a stride of 5 float elements
552     glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
553     // bind 2 elements, starting after the first 3; same stride of 5 float elements
554     glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
555                           reinterpret_cast<const GLvoid *>(3 * 4));
556 
557     glEnableVertexAttribArray(kOneLoc);
558     glEnableVertexAttribArray(kTwoLoc);
559 
560     // set test uniform
561     GLint uniLoc = glGetUniformLocation(program, "comparison");
562     ASSERT_NE(-1, uniLoc);
563     glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
564 
565     // Draw out of bounds.
566     glDrawArrays(GL_TRIANGLES, 0, 10000);
567     GLenum err = glGetError();
568     if (err == GL_NO_ERROR)
569     {
570         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
571     }
572     else
573     {
574         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
575     }
576 
577     glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
578     err = glGetError();
579     if (err == GL_NO_ERROR)
580     {
581         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
582     }
583     else
584     {
585         EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
586     }
587 }
588 
589 // Tests out of bounds read by divisor emulation due to a user-provided offset.
590 // Adapted from https://crbug.com/1285885.
TEST_P(RobustBufferAccessBehaviorTest,IndexOutOfBounds)591 TEST_P(RobustBufferAccessBehaviorTest, IndexOutOfBounds)
592 {
593     ANGLE_SKIP_TEST_IF(!initExtension());
594 
595     constexpr char kVS[] = R"(precision highp float;
596 attribute vec4 a_position;
597 void main(void) {
598    gl_Position = a_position;
599 })";
600 
601     constexpr char kFS[] = R"(precision highp float;
602 uniform sampler2D oTexture;
603 uniform float oColor[3];
604 void main(void) {
605    gl_FragData[0] = texture2DProj(oTexture, vec3(0.1,0.1,0.1));
606 })";
607 
608     GLfloat singleFloat = 1.0f;
609 
610     GLBuffer buf;
611     glBindBuffer(GL_ARRAY_BUFFER, buf);
612     glBufferData(GL_ARRAY_BUFFER, 4, &singleFloat, GL_STATIC_DRAW);
613 
614     ANGLE_GL_PROGRAM(program, kVS, kFS);
615     glBindAttribLocation(program, 0, "a_position");
616     glLinkProgram(program);
617     ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
618 
619     glEnableVertexAttribArray(0);
620 
621     // Trying to exceed renderer->getMaxVertexAttribDivisor()
622     GLuint constexpr kDivisor = 4096;
623     glVertexAttribDivisor(0, kDivisor);
624 
625     size_t outOfBoundsOffset = 0x50000000;
626     glVertexAttribPointer(0, 1, GL_FLOAT, false, 8, reinterpret_cast<void *>(outOfBoundsOffset));
627 
628     glUseProgram(program);
629 
630     glDrawArrays(GL_TRIANGLES, 0, 32);
631 
632     // No assertions, just checking for crashes.
633 }
634 
635 // Similar to the test above but index is first within bounds then goes out of bounds.
TEST_P(RobustBufferAccessBehaviorTest,IndexGoingOutOfBounds)636 TEST_P(RobustBufferAccessBehaviorTest, IndexGoingOutOfBounds)
637 {
638     ANGLE_SKIP_TEST_IF(!initExtension());
639 
640     constexpr char kVS[] = R"(precision highp float;
641 attribute vec4 a_position;
642 void main(void) {
643    gl_Position = a_position;
644 })";
645 
646     constexpr char kFS[] = R"(precision highp float;
647 uniform sampler2D oTexture;
648 uniform float oColor[3];
649 void main(void) {
650    gl_FragData[0] = texture2DProj(oTexture, vec3(0.1,0.1,0.1));
651 })";
652 
653     GLBuffer buf;
654     glBindBuffer(GL_ARRAY_BUFFER, buf);
655     std::array<GLfloat, 2> buffer = {{0.2f, 0.2f}};
656     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * buffer.size(), buffer.data(), GL_STATIC_DRAW);
657 
658     ANGLE_GL_PROGRAM(program, kVS, kFS);
659     glBindAttribLocation(program, 0, "a_position");
660     glLinkProgram(program);
661     ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
662 
663     glEnableVertexAttribArray(0);
664 
665     // Trying to exceed renderer->getMaxVertexAttribDivisor()
666     GLuint constexpr kDivisor = 4096;
667     glVertexAttribDivisor(0, kDivisor);
668 
669     // 6 bytes remaining in the buffer from offset so only a single vertex can be read
670     glVertexAttribPointer(0, 1, GL_FLOAT, false, 8, reinterpret_cast<void *>(2));
671 
672     glUseProgram(program);
673 
674     // Each vertex is read `kDivisor` times so the last read goes out of bounds
675     GLsizei instanceCount = kDivisor + 1;
676     glDrawArraysInstanced(GL_TRIANGLES, 0, 32, instanceCount);
677 
678     // No assertions, just checking for crashes.
679 }
680 
681 ANGLE_INSTANTIATE_TEST(RobustBufferAccessBehaviorTest,
682                        WithNoFixture(ES3_VULKAN()),
683                        WithNoFixture(ES3_OPENGL()),
684                        WithNoFixture(ES3_OPENGLES()),
685                        WithNoFixture(ES3_D3D11()),
686                        WithNoFixture(ES3_METAL()));
687 
688 }  // namespace
689