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
14 #include <array>
15
16 using namespace angle;
17
18 namespace
19 {
20
21 class RobustBufferAccessBehaviorTest : public ANGLETest
22 {
23 protected:
RobustBufferAccessBehaviorTest()24 RobustBufferAccessBehaviorTest() : mProgram(0), mTestAttrib(-1)
25 {
26 setWindowWidth(128);
27 setWindowHeight(128);
28 setConfigRedBits(8);
29 setConfigGreenBits(8);
30 setConfigBlueBits(8);
31 setConfigAlphaBits(8);
32
33 // Test flakiness was noticed when reusing displays.
34 forceNewDisplay();
35 }
36
testTearDown()37 void testTearDown() override { glDeleteProgram(mProgram); }
38
initExtension()39 bool initExtension()
40 {
41 EGLWindow *window = getEGLWindow();
42 EGLDisplay display = window->getDisplay();
43 if (!IsEGLDisplayExtensionEnabled(display, "EGL_EXT_create_context_robustness"))
44 {
45 return false;
46 }
47 setRobustAccess(true);
48 if (!IsGLExtensionEnabled("GL_KHR_robust_buffer_access_behavior"))
49 {
50 return false;
51 }
52 return true;
53 }
54
initBasicProgram()55 void initBasicProgram()
56 {
57 constexpr char kVS[] =
58 "precision mediump float;\n"
59 "attribute vec4 position;\n"
60 "attribute vec4 vecRandom;\n"
61 "varying vec4 v_color;\n"
62 "bool testFloatComponent(float component) {\n"
63 " return (component == 0.2 || component == 0.0);\n"
64 "}\n"
65 "bool testLastFloatComponent(float component) {\n"
66 " return testFloatComponent(component) || component == 1.0;\n"
67 "}\n"
68 "void main() {\n"
69 " if (testFloatComponent(vecRandom.x) &&\n"
70 " testFloatComponent(vecRandom.y) &&\n"
71 " testFloatComponent(vecRandom.z) &&\n"
72 " testLastFloatComponent(vecRandom.w)) {\n"
73 " v_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
74 " } else {\n"
75 " v_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
76 " }\n"
77 " gl_Position = position;\n"
78 "}\n";
79
80 constexpr char kFS[] =
81 "precision mediump float;\n"
82 "varying vec4 v_color;\n"
83 "void main() {\n"
84 " gl_FragColor = v_color;\n"
85 "}\n";
86
87 mProgram = CompileProgram(kVS, kFS);
88 ASSERT_NE(0u, mProgram);
89
90 mTestAttrib = glGetAttribLocation(mProgram, "vecRandom");
91 ASSERT_NE(-1, mTestAttrib);
92
93 glUseProgram(mProgram);
94 }
95
runIndexOutOfRangeTests(GLenum drawType)96 void runIndexOutOfRangeTests(GLenum drawType)
97 {
98 if (mProgram == 0)
99 {
100 initBasicProgram();
101 }
102
103 GLBuffer bufferIncomplete;
104 glBindBuffer(GL_ARRAY_BUFFER, bufferIncomplete);
105 std::array<GLfloat, 12> randomData = {
106 {0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f}};
107 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * randomData.size(), randomData.data(),
108 drawType);
109
110 glEnableVertexAttribArray(mTestAttrib);
111 glVertexAttribPointer(mTestAttrib, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
112
113 glClearColor(0.0, 0.0, 1.0, 1.0);
114 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
115
116 drawIndexedQuad(mProgram, "position", 0.5f);
117
118 int width = getWindowWidth();
119 int height = getWindowHeight();
120 GLenum result = glGetError();
121 // For D3D dynamic draw, we still return invalid operation. Once we force the index buffer
122 // to clamp any out of range indices instead of invalid operation, this part can be removed.
123 // We can always get GL_NO_ERROR.
124 if (result == GL_INVALID_OPERATION)
125 {
126 EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::blue);
127 EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::blue);
128 EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::blue);
129 EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::blue);
130 }
131 else
132 {
133 EXPECT_GLENUM_EQ(GL_NO_ERROR, result);
134 EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::green);
135 EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::green);
136 EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::green);
137 EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::green);
138 }
139 }
140
141 GLuint mProgram;
142 GLint mTestAttrib;
143 };
144
145 // Test that static draw with out-of-bounds reads will not read outside of the data store of the
146 // buffer object and will not result in GL interruption or termination when
147 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithStaticDraw)148 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithStaticDraw)
149 {
150 ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
151
152 // Failing on NV after changing shard count of angle_end2end_tests. http://anglebug.com/2799
153 // Also failing on AMD after a validation change. http://anglebug.com/3042
154 ANGLE_SKIP_TEST_IF(IsD3D11_FL93());
155
156 ANGLE_SKIP_TEST_IF(!initExtension());
157
158 runIndexOutOfRangeTests(GL_STATIC_DRAW);
159 }
160
161 // Test that dynamic draw with out-of-bounds reads will not read outside of the data store of the
162 // buffer object and will not result in GL interruption or termination when
163 // GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest,DrawElementsIndexOutOfRangeWithDynamicDraw)164 TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithDynamicDraw)
165 {
166 ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
167 ANGLE_SKIP_TEST_IF(!initExtension());
168
169 runIndexOutOfRangeTests(GL_DYNAMIC_DRAW);
170 }
171
172 // Test that vertex buffers are rebound with the correct offsets in subsequent calls in the D3D11
173 // backend. http://crbug.com/837002
TEST_P(RobustBufferAccessBehaviorTest,D3D11StateSynchronizationOrderBug)174 TEST_P(RobustBufferAccessBehaviorTest, D3D11StateSynchronizationOrderBug)
175 {
176 ANGLE_SKIP_TEST_IF(!initExtension());
177
178 glDisable(GL_DEPTH_TEST);
179
180 // 2 quads, the first one red, the second one green
181 const std::array<angle::Vector4, 16> vertices{
182 angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f), // v0
183 angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f), // c0
184 angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f), // v1
185 angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f), // c1
186 angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f), // v2
187 angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f), // c2
188 angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f), // v3
189 angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f), // c3
190
191 angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f), // v4
192 angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f), // c4
193 angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f), // v5
194 angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f), // c5
195 angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f), // v6
196 angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f), // c6
197 angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f), // v7
198 angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f), // c7
199 };
200
201 GLBuffer vb;
202 glBindBuffer(GL_ARRAY_BUFFER, vb);
203 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
204
205 const std::array<GLushort, 12> indicies{
206 0, 1, 2, 0, 2, 3, // quad0
207 4, 5, 6, 4, 6, 7, // quad1
208 };
209
210 GLBuffer ib;
211 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
212 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies.data(), GL_STATIC_DRAW);
213
214 constexpr char kVS[] = R"(
215 precision highp float;
216 attribute vec4 a_position;
217 attribute vec4 a_color;
218
219 varying vec4 v_color;
220
221 void main()
222 {
223 gl_Position = a_position;
224 v_color = a_color;
225 })";
226
227 constexpr char kFS[] = R"(
228 precision highp float;
229 varying vec4 v_color;
230
231 void main()
232 {
233 gl_FragColor = v_color;
234 })";
235
236 ANGLE_GL_PROGRAM(program, kVS, kFS);
237 glUseProgram(program);
238
239 GLint positionLocation = glGetAttribLocation(program, "a_position");
240 glEnableVertexAttribArray(positionLocation);
241 glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2, 0);
242
243 GLint colorLocation = glGetAttribLocation(program, "a_color");
244 glEnableVertexAttribArray(colorLocation);
245 glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2,
246 reinterpret_cast<const void *>(sizeof(angle::Vector4)));
247
248 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
249 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
250
251 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
252 reinterpret_cast<const void *>(sizeof(GLshort) * 6));
253 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
254
255 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
256 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
257
258 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
259 reinterpret_cast<const void *>(sizeof(GLshort) * 6));
260 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
261 }
262
263 // Covers drawing with a very large vertex range which overflows GLsizei. http://crbug.com/842028
TEST_P(RobustBufferAccessBehaviorTest,VeryLargeVertexCountWithDynamicVertexData)264 TEST_P(RobustBufferAccessBehaviorTest, VeryLargeVertexCountWithDynamicVertexData)
265 {
266 ANGLE_SKIP_TEST_IF(!initExtension());
267 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_element_index_uint"));
268
269 constexpr GLsizei kIndexCount = 32;
270 std::array<GLuint, kIndexCount> indices = {{}};
271 for (GLsizei index = 0; index < kIndexCount; ++index)
272 {
273 indices[index] = ((std::numeric_limits<GLuint>::max() - 2) / kIndexCount) * index;
274 }
275
276 GLBuffer indexBuffer;
277 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
278 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(),
279 GL_STATIC_DRAW);
280
281 std::array<GLfloat, 256> vertexData = {{}};
282
283 GLBuffer vertexBuffer;
284 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
285 glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(GLfloat), vertexData.data(),
286 GL_DYNAMIC_DRAW);
287
288 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
289 glUseProgram(program);
290
291 GLint attribLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
292 ASSERT_NE(-1, attribLoc);
293
294 glVertexAttribPointer(attribLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
295 glEnableVertexAttribArray(attribLoc);
296 ASSERT_GL_NO_ERROR();
297
298 glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_INT, nullptr);
299
300 // This may or may not generate an error, but it should not crash.
301 }
302
303 // Test that robust access works even if there's no data uploaded to the vertex buffer at all.
TEST_P(RobustBufferAccessBehaviorTest,NoBufferData)304 TEST_P(RobustBufferAccessBehaviorTest, NoBufferData)
305 {
306 // http://crbug.com/889303: Possible driver bug on NVIDIA Shield TV.
307 // http://anglebug.com/2861: Fails abnormally on Android
308 ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
309
310 ANGLE_SKIP_TEST_IF(!initExtension());
311 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
312 glUseProgram(program);
313
314 glEnableVertexAttribArray(0);
315 GLBuffer buf;
316 glBindBuffer(GL_ARRAY_BUFFER, buf);
317
318 glVertexAttribPointer(0, 1, GL_FLOAT, false, 0, nullptr);
319 ASSERT_GL_NO_ERROR();
320
321 std::array<GLubyte, 1u> indices = {0};
322 glDrawElements(GL_POINTS, indices.size(), GL_UNSIGNED_BYTE, indices.data());
323 ASSERT_GL_NO_ERROR();
324 }
325
326 constexpr char kWebGLVS[] = R"(attribute vec2 position;
327 attribute vec4 aOne;
328 attribute vec4 aTwo;
329 varying vec4 v;
330 uniform vec2 comparison;
331
332 bool isRobust(vec4 value) {
333 // The valid buffer range is filled with this value.
334 if (value.xy == comparison)
335 return true;
336 // Checking the w value is a bit complex.
337 return (value.xyz == vec3(0, 0, 0));
338 }
339
340 void main() {
341 gl_Position = vec4(position, 0, 1);
342 if (isRobust(aOne) && isRobust(aTwo)) {
343 v = vec4(0, 1, 0, 1);
344 } else {
345 v = vec4(1, 0, 0, 1);
346 }
347 })";
348
349 constexpr char kWebGLFS[] = R"(precision mediump float;
350 varying vec4 v;
351 void main() {
352 gl_FragColor = v;
353 })";
354
355 // Test buffer with interleaved (3+2) float vectors. Adapted from WebGL test
356 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,InterleavedAttributes)357 TEST_P(RobustBufferAccessBehaviorTest, InterleavedAttributes)
358 {
359 ANGLE_SKIP_TEST_IF(!initExtension());
360
361 ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
362 glUseProgram(program);
363
364 constexpr GLint kPosLoc = 0;
365 constexpr GLint kOneLoc = 1;
366 constexpr GLint kTwoLoc = 2;
367
368 ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
369 ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
370 ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
371
372 // Create a buffer of 200 valid sets of quad lists.
373 constexpr size_t kNumQuads = 200;
374 using QuadVerts = std::array<Vector3, 6>;
375 std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
376
377 GLBuffer positionBuf;
378 glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
379 glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
380 glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
381 glEnableVertexAttribArray(kPosLoc);
382
383 constexpr GLfloat kDefaultFloat = 0.2f;
384 std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
385
386 GLBuffer vbo;
387 glBindBuffer(GL_ARRAY_BUFFER, vbo);
388
389 // enough for 9 vertices, so 3 triangles
390 glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_STATIC_DRAW);
391
392 // bind first 3 elements, with a stride of 5 float elements
393 glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
394 // bind 2 elements, starting after the first 3; same stride of 5 float elements
395 glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
396 reinterpret_cast<const GLvoid *>(3 * 4));
397
398 glEnableVertexAttribArray(kOneLoc);
399 glEnableVertexAttribArray(kTwoLoc);
400
401 // set test uniform
402 GLint uniLoc = glGetUniformLocation(program, "comparison");
403 ASSERT_NE(-1, uniLoc);
404 glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
405
406 // Draw out of bounds.
407 glDrawArrays(GL_TRIANGLES, 0, 10000);
408 GLenum err = glGetError();
409 if (err == GL_NO_ERROR)
410 {
411 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
412 }
413 else
414 {
415 EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
416 }
417
418 glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
419 err = glGetError();
420 if (err == GL_NO_ERROR)
421 {
422 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
423 }
424 else
425 {
426 EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
427 }
428 }
429
430 // Tests redefining an empty buffer. Adapted from WebGL test
431 // conformance/rendering/draw-arrays-out-of-bounds.html
TEST_P(RobustBufferAccessBehaviorTest,EmptyBuffer)432 TEST_P(RobustBufferAccessBehaviorTest, EmptyBuffer)
433 {
434 ANGLE_SKIP_TEST_IF(!initExtension());
435
436 // AMD GL does not support robustness. http://anglebug.com/3099
437 ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
438
439 // http://anglebug.com/2861: Fails abnormally on Android
440 ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
441
442 ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
443 glUseProgram(program);
444
445 constexpr GLint kPosLoc = 0;
446 constexpr GLint kOneLoc = 1;
447 constexpr GLint kTwoLoc = 2;
448
449 ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
450 ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
451 ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
452
453 // Create a buffer of 200 valid sets of quad lists.
454 constexpr size_t kNumQuads = 200;
455 using QuadVerts = std::array<Vector3, 6>;
456 std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
457
458 GLBuffer positionBuf;
459 glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
460 glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
461 glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
462 glEnableVertexAttribArray(kPosLoc);
463
464 // set test uniform
465 GLint uniLoc = glGetUniformLocation(program, "comparison");
466 ASSERT_NE(-1, uniLoc);
467 glUniform2f(uniLoc, 0, 0);
468
469 // Define empty buffer.
470 GLBuffer buffer;
471 glBindBuffer(GL_ARRAY_BUFFER, buffer);
472 glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
473 glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
474 glEnableVertexAttribArray(kOneLoc);
475 glDrawArrays(GL_TRIANGLES, 0, 3);
476 GLenum err = glGetError();
477 if (err == GL_NO_ERROR)
478 {
479 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
480 }
481 else
482 {
483 EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
484 }
485
486 // Redefine buffer with 3 float vectors.
487 constexpr GLfloat kFloats[] = {0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0};
488 glBufferData(GL_ARRAY_BUFFER, sizeof(kFloats), kFloats, GL_STATIC_DRAW);
489 glDrawArrays(GL_TRIANGLES, 0, 3);
490 ASSERT_GL_NO_ERROR();
491 }
492
493 // Tests robust buffer access with dynamic buffer usage.
TEST_P(RobustBufferAccessBehaviorTest,DynamicBuffer)494 TEST_P(RobustBufferAccessBehaviorTest, DynamicBuffer)
495 {
496 ANGLE_SKIP_TEST_IF(!initExtension());
497
498 ANGLE_GL_PROGRAM(program, kWebGLVS, kWebGLFS);
499 glUseProgram(program);
500
501 constexpr GLint kPosLoc = 0;
502 constexpr GLint kOneLoc = 1;
503 constexpr GLint kTwoLoc = 2;
504
505 ASSERT_EQ(kPosLoc, glGetAttribLocation(program, "position"));
506 ASSERT_EQ(kOneLoc, glGetAttribLocation(program, "aOne"));
507 ASSERT_EQ(kTwoLoc, glGetAttribLocation(program, "aTwo"));
508
509 // Create a buffer of 200 valid sets of quad lists.
510 constexpr size_t kNumQuads = 200;
511 using QuadVerts = std::array<Vector3, 6>;
512 std::vector<QuadVerts> quadVerts(kNumQuads, GetQuadVertices());
513
514 GLBuffer positionBuf;
515 glBindBuffer(GL_ARRAY_BUFFER, positionBuf);
516 glBufferData(GL_ARRAY_BUFFER, kNumQuads * sizeof(QuadVerts), quadVerts.data(), GL_STATIC_DRAW);
517 glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
518 glEnableVertexAttribArray(kPosLoc);
519
520 constexpr GLfloat kDefaultFloat = 0.2f;
521 std::vector<Vector4> defaultFloats(kNumQuads * 2, Vector4(kDefaultFloat));
522
523 GLBuffer vbo;
524 glBindBuffer(GL_ARRAY_BUFFER, vbo);
525
526 // enough for 9 vertices, so 3 triangles
527 glBufferData(GL_ARRAY_BUFFER, 9 * 5 * sizeof(GLfloat), defaultFloats.data(), GL_DYNAMIC_DRAW);
528
529 // bind first 3 elements, with a stride of 5 float elements
530 glVertexAttribPointer(kOneLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0);
531 // bind 2 elements, starting after the first 3; same stride of 5 float elements
532 glVertexAttribPointer(kTwoLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4,
533 reinterpret_cast<const GLvoid *>(3 * 4));
534
535 glEnableVertexAttribArray(kOneLoc);
536 glEnableVertexAttribArray(kTwoLoc);
537
538 // set test uniform
539 GLint uniLoc = glGetUniformLocation(program, "comparison");
540 ASSERT_NE(-1, uniLoc);
541 glUniform2f(uniLoc, kDefaultFloat, kDefaultFloat);
542
543 // Draw out of bounds.
544 glDrawArrays(GL_TRIANGLES, 0, 10000);
545 GLenum err = glGetError();
546 if (err == GL_NO_ERROR)
547 {
548 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
549 }
550 else
551 {
552 EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
553 }
554
555 glDrawArrays(GL_TRIANGLES, (kNumQuads - 1) * 6, 6);
556 err = glGetError();
557 if (err == GL_NO_ERROR)
558 {
559 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
560 }
561 else
562 {
563 EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, err);
564 }
565 }
566
567 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND_ES31(RobustBufferAccessBehaviorTest);
568
569 } // namespace
570