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