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 ¶ms = 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