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.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