1 //
2 // Copyright 2015 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
7 #include "test_utils/ANGLETest.h"
8 #include "test_utils/gl_raii.h"
9
10 #include <stdint.h>
11
12 using namespace angle;
13
14 class BufferDataTest : public ANGLETest
15 {
16 protected:
BufferDataTest()17 BufferDataTest()
18 {
19 setWindowWidth(16);
20 setWindowHeight(16);
21 setConfigRedBits(8);
22 setConfigGreenBits(8);
23 setConfigBlueBits(8);
24 setConfigAlphaBits(8);
25 setConfigDepthBits(24);
26
27 mBuffer = 0;
28 mProgram = 0;
29 mAttribLocation = -1;
30 }
31
SetUp()32 void SetUp() override
33 {
34 ANGLETest::SetUp();
35
36 const char * vsSource = SHADER_SOURCE
37 (
38 attribute vec4 position;
39 attribute float in_attrib;
40 varying float v_attrib;
41 void main()
42 {
43 v_attrib = in_attrib;
44 gl_Position = position;
45 }
46 );
47
48 const char * fsSource = SHADER_SOURCE
49 (
50 precision mediump float;
51 varying float v_attrib;
52 void main()
53 {
54 gl_FragColor = vec4(v_attrib, 0, 0, 1);
55 }
56 );
57
58 glGenBuffers(1, &mBuffer);
59 ASSERT_NE(mBuffer, 0U);
60
61 mProgram = CompileProgram(vsSource, fsSource);
62 ASSERT_NE(mProgram, 0U);
63
64 mAttribLocation = glGetAttribLocation(mProgram, "in_attrib");
65 ASSERT_NE(mAttribLocation, -1);
66
67 glClearColor(0, 0, 0, 0);
68 glClearDepthf(0.0);
69 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
70
71 glDisable(GL_DEPTH_TEST);
72
73 ASSERT_GL_NO_ERROR();
74 }
75
TearDown()76 void TearDown() override
77 {
78 glDeleteBuffers(1, &mBuffer);
79 glDeleteProgram(mProgram);
80
81 ANGLETest::TearDown();
82 }
83
84 GLuint mBuffer;
85 GLuint mProgram;
86 GLint mAttribLocation;
87 };
88
TEST_P(BufferDataTest,NULLData)89 TEST_P(BufferDataTest, NULLData)
90 {
91 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
92 EXPECT_GL_NO_ERROR();
93
94 const int numIterations = 128;
95 for (int i = 0; i < numIterations; ++i)
96 {
97 GLsizei bufferSize = sizeof(GLfloat) * (i + 1);
98 glBufferData(GL_ARRAY_BUFFER, bufferSize, NULL, GL_STATIC_DRAW);
99 EXPECT_GL_NO_ERROR();
100
101 for (int j = 0; j < bufferSize; j++)
102 {
103 for (int k = 0; k < bufferSize - j; k++)
104 {
105 glBufferSubData(GL_ARRAY_BUFFER, k, j, NULL);
106 ASSERT_GL_NO_ERROR();
107 }
108 }
109 }
110 }
111
TEST_P(BufferDataTest,ZeroNonNULLData)112 TEST_P(BufferDataTest, ZeroNonNULLData)
113 {
114 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
115 EXPECT_GL_NO_ERROR();
116
117 char *zeroData = new char[0];
118 glBufferData(GL_ARRAY_BUFFER, 0, zeroData, GL_STATIC_DRAW);
119 EXPECT_GL_NO_ERROR();
120
121 glBufferSubData(GL_ARRAY_BUFFER, 0, 0, zeroData);
122 EXPECT_GL_NO_ERROR();
123
124 delete [] zeroData;
125 }
126
TEST_P(BufferDataTest,NULLResolvedData)127 TEST_P(BufferDataTest, NULLResolvedData)
128 {
129 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
130 glBufferData(GL_ARRAY_BUFFER, 128, NULL, GL_DYNAMIC_DRAW);
131
132 glUseProgram(mProgram);
133 glVertexAttribPointer(mAttribLocation, 1, GL_FLOAT, GL_FALSE, 4, NULL);
134 glEnableVertexAttribArray(mAttribLocation);
135 glBindBuffer(GL_ARRAY_BUFFER, 0);
136
137 drawQuad(mProgram, "position", 0.5f);
138 }
139
140 // Tests that a huge allocation returns GL_OUT_OF_MEMORY
141 // TODO(jmadill): Figure out how to test this reliably on the Chromium bots
TEST_P(BufferDataTest,DISABLED_HugeSetDataShouldNotCrash)142 TEST_P(BufferDataTest, DISABLED_HugeSetDataShouldNotCrash)
143 {
144 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
145 EXPECT_GL_NO_ERROR();
146
147 GLsizei allocSize = std::numeric_limits<GLsizei>::max() >> 2;
148
149 uint8_t *data = NULL;
150 while (data == NULL && allocSize >= 4)
151 {
152 data = new (std::nothrow) uint8_t[allocSize];
153
154 if (data == NULL)
155 {
156 allocSize >>= 1;
157 }
158 }
159
160 ASSERT_NE(static_cast<uint8_t*>(NULL), data);
161 memset(data, 0, allocSize);
162
163 float * fValue = reinterpret_cast<float*>(data);
164 for (unsigned int f = 0; f < 6; f++)
165 {
166 fValue[f] = 1.0f;
167 }
168
169 glBufferData(GL_ARRAY_BUFFER, allocSize, data, GL_STATIC_DRAW);
170
171 GLenum error = glGetError();
172 if (error == GL_NO_ERROR)
173 {
174 // If we didn't fail because of an out of memory error, try drawing a quad
175 // using the large buffer
176
177 // DISABLED because it takes a long time, but left for posterity
178
179 //glUseProgram(mProgram);
180 //glVertexAttribPointer(mAttribLocation, 1, GL_FLOAT, GL_FALSE, 4, NULL);
181 //glEnableVertexAttribArray(mAttribLocation);
182 //glBindBuffer(GL_ARRAY_BUFFER, 0);
183 //drawQuad(mProgram, "position", 0.5f);
184 //swapBuffers();
185
186 //// Draw operations can also generate out-of-memory, which is in-spec
187 //error = glGetError();
188 //if (error == GL_NO_ERROR)
189 //{
190 // GLint viewportSize[4];
191 // glGetIntegerv(GL_VIEWPORT, viewportSize);
192
193 // GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
194 // GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;
195
196 // EXPECT_PIXEL_EQ(midPixelX, midPixelY, 255, 0, 0, 255);
197 //}
198 //else
199 //{
200 // EXPECT_EQ(GL_OUT_OF_MEMORY, error);
201 //}
202 }
203 else
204 {
205 EXPECT_GLENUM_EQ(GL_OUT_OF_MEMORY, error);
206 }
207
208 delete[] data;
209 }
210
211 // Internally in D3D, we promote dynamic data to static after many draw loops. This code tests
212 // path.
TEST_P(BufferDataTest,RepeatedDrawWithDynamic)213 TEST_P(BufferDataTest, RepeatedDrawWithDynamic)
214 {
215 std::vector<GLfloat> data;
216 for (int i = 0; i < 16; ++i)
217 {
218 data.push_back(static_cast<GLfloat>(i));
219 }
220
221 glUseProgram(mProgram);
222 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
223 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), data.data(), GL_DYNAMIC_DRAW);
224 glVertexAttribPointer(mAttribLocation, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
225 glBindBuffer(GL_ARRAY_BUFFER, 0);
226 glEnableVertexAttribArray(mAttribLocation);
227
228 for (int drawCount = 0; drawCount < 40; ++drawCount)
229 {
230 drawQuad(mProgram, "position", 0.5f);
231 }
232
233 EXPECT_GL_NO_ERROR();
234 }
235
236 class IndexedBufferCopyTest : public ANGLETest
237 {
238 protected:
IndexedBufferCopyTest()239 IndexedBufferCopyTest()
240 {
241 setWindowWidth(16);
242 setWindowHeight(16);
243 setConfigRedBits(8);
244 setConfigGreenBits(8);
245 setConfigBlueBits(8);
246 setConfigAlphaBits(8);
247 setConfigDepthBits(24);
248 }
249
SetUp()250 void SetUp() override
251 {
252 ANGLETest::SetUp();
253
254 const char * vsSource = SHADER_SOURCE
255 (
256 attribute vec3 in_attrib;
257 varying vec3 v_attrib;
258 void main()
259 {
260 v_attrib = in_attrib;
261 gl_Position = vec4(0.0, 0.0, 0.5, 1.0);
262 gl_PointSize = 100.0;
263 }
264 );
265
266 const char * fsSource = SHADER_SOURCE
267 (
268 precision mediump float;
269 varying vec3 v_attrib;
270 void main()
271 {
272 gl_FragColor = vec4(v_attrib, 1);
273 }
274 );
275
276 glGenBuffers(2, mBuffers);
277 ASSERT_NE(mBuffers[0], 0U);
278 ASSERT_NE(mBuffers[1], 0U);
279
280 glGenBuffers(1, &mElementBuffer);
281 ASSERT_NE(mElementBuffer, 0U);
282
283 mProgram = CompileProgram(vsSource, fsSource);
284 ASSERT_NE(mProgram, 0U);
285
286 mAttribLocation = glGetAttribLocation(mProgram, "in_attrib");
287 ASSERT_NE(mAttribLocation, -1);
288
289 glClearColor(0, 0, 0, 0);
290 glDisable(GL_DEPTH_TEST);
291 glClear(GL_COLOR_BUFFER_BIT);
292
293 ASSERT_GL_NO_ERROR();
294 }
295
TearDown()296 void TearDown() override
297 {
298 glDeleteBuffers(2, mBuffers);
299 glDeleteBuffers(1, &mElementBuffer);
300 glDeleteProgram(mProgram);
301
302 ANGLETest::TearDown();
303 }
304
305 GLuint mBuffers[2];
306 GLuint mElementBuffer;
307 GLuint mProgram;
308 GLint mAttribLocation;
309 };
310
311 // The following test covers an ANGLE bug where our index ranges
312 // weren't updated from CopyBufferSubData calls
313 // https://code.google.com/p/angleproject/issues/detail?id=709
TEST_P(IndexedBufferCopyTest,IndexRangeBug)314 TEST_P(IndexedBufferCopyTest, IndexRangeBug)
315 {
316 // TODO(geofflang): Figure out why this fails on AMD OpenGL (http://anglebug.com/1291)
317 if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
318 {
319 std::cout << "Test disabled on AMD OpenGL." << std::endl;
320 return;
321 }
322
323 unsigned char vertexData[] = { 255, 0, 0, 0, 0, 0 };
324 unsigned int indexData[] = { 0, 1 };
325
326 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[0]);
327 glBufferData(GL_ARRAY_BUFFER, sizeof(char) * 6, vertexData, GL_STATIC_DRAW);
328
329 glUseProgram(mProgram);
330 glVertexAttribPointer(mAttribLocation, 3, GL_UNSIGNED_BYTE, GL_TRUE, 3, NULL);
331 glEnableVertexAttribArray(mAttribLocation);
332
333 ASSERT_GL_NO_ERROR();
334
335 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementBuffer);
336 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * 1, indexData, GL_STATIC_DRAW);
337
338 glUseProgram(mProgram);
339
340 ASSERT_GL_NO_ERROR();
341
342 glDrawElements(GL_POINTS, 1, GL_UNSIGNED_INT, NULL);
343
344 EXPECT_GL_NO_ERROR();
345 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
346
347 glBindBuffer(GL_COPY_READ_BUFFER, mBuffers[1]);
348 glBufferData(GL_COPY_READ_BUFFER, 4, &indexData[1], GL_STATIC_DRAW);
349
350 glBindBuffer(GL_COPY_WRITE_BUFFER, mElementBuffer);
351
352 glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(int));
353
354 ASSERT_GL_NO_ERROR();
355
356 glClear(GL_COLOR_BUFFER_BIT);
357 EXPECT_PIXEL_EQ(0, 0, 0, 0, 0, 0);
358
359 unsigned char newData[] = { 0, 255, 0 };
360 glBufferSubData(GL_ARRAY_BUFFER, 3, 3, newData);
361
362 glDrawElements(GL_POINTS, 1, GL_UNSIGNED_INT, NULL);
363
364 EXPECT_GL_NO_ERROR();
365 EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255);
366 }
367
368 class BufferDataTestES3 : public BufferDataTest
369 {
370 };
371
372 // The following test covers an ANGLE bug where the buffer storage
373 // is not resized by Buffer11::getLatestBufferStorage when needed.
374 // https://code.google.com/p/angleproject/issues/detail?id=897
TEST_P(BufferDataTestES3,BufferResizing)375 TEST_P(BufferDataTestES3, BufferResizing)
376 {
377 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
378 ASSERT_GL_NO_ERROR();
379
380 // Allocate a buffer with one byte
381 uint8_t singleByte[] = { 0xaa };
382 glBufferData(GL_ARRAY_BUFFER, 1, singleByte, GL_STATIC_DRAW);
383
384 // Resize the buffer
385 // To trigger the bug, the buffer need to be big enough because some hardware copy buffers
386 // by chunks of pages instead of the minimum number of bytes neeeded.
387 const size_t numBytes = 4096*4;
388 glBufferData(GL_ARRAY_BUFFER, numBytes, NULL, GL_STATIC_DRAW);
389
390 // Copy the original data to the buffer
391 uint8_t srcBytes[numBytes];
392 for (size_t i = 0; i < numBytes; ++i)
393 {
394 srcBytes[i] = static_cast<uint8_t>(i);
395 }
396
397 void *dest = glMapBufferRange(GL_ARRAY_BUFFER, 0, numBytes, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
398
399 ASSERT_GL_NO_ERROR();
400
401 memcpy(dest, srcBytes, numBytes);
402 glUnmapBuffer(GL_ARRAY_BUFFER);
403
404 EXPECT_GL_NO_ERROR();
405
406 // Create a new buffer and copy the data to it
407 GLuint readBuffer;
408 glGenBuffers(1, &readBuffer);
409 glBindBuffer(GL_COPY_WRITE_BUFFER, readBuffer);
410 uint8_t zeros[numBytes];
411 for (size_t i = 0; i < numBytes; ++i)
412 {
413 zeros[i] = 0;
414 }
415 glBufferData(GL_COPY_WRITE_BUFFER, numBytes, zeros, GL_STATIC_DRAW);
416 glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, numBytes);
417
418 ASSERT_GL_NO_ERROR();
419
420 // Read back the data and compare it to the original
421 uint8_t *data = reinterpret_cast<uint8_t*>(glMapBufferRange(GL_COPY_WRITE_BUFFER, 0, numBytes, GL_MAP_READ_BIT));
422
423 ASSERT_GL_NO_ERROR();
424
425 for (size_t i = 0; i < numBytes; ++i)
426 {
427 EXPECT_EQ(srcBytes[i], data[i]);
428 }
429 glUnmapBuffer(GL_COPY_WRITE_BUFFER);
430
431 glDeleteBuffers(1, &readBuffer);
432
433 EXPECT_GL_NO_ERROR();
434 }
435
436 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
437 ANGLE_INSTANTIATE_TEST(BufferDataTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES());
438 ANGLE_INSTANTIATE_TEST(BufferDataTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
439 ANGLE_INSTANTIATE_TEST(IndexedBufferCopyTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
440
441 #ifdef _WIN64
442
443 // Test a bug where an integer overflow bug could trigger a crash in D3D.
444 // The test uses 8 buffers with a size just under 0x2000000 to overflow max uint
445 // (with the internal D3D rounding to 16-byte values) and trigger the bug.
446 // Only handle this bug on 64-bit Windows for now. Harder to repro on 32-bit.
447 class BufferDataOverflowTest : public ANGLETest
448 {
449 protected:
BufferDataOverflowTest()450 BufferDataOverflowTest()
451 {
452 }
453 };
454
455 // See description above.
TEST_P(BufferDataOverflowTest,VertexBufferIntegerOverflow)456 TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow)
457 {
458 // These values are special, to trigger the rounding bug.
459 unsigned int numItems = 0x7FFFFFE;
460 constexpr GLsizei bufferCnt = 8;
461
462 std::vector<GLBuffer> buffers(bufferCnt);
463
464 std::stringstream vertexShaderStr;
465
466 for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
467 {
468 vertexShaderStr << "attribute float attrib" << bufferIndex << ";\n";
469 }
470
471 vertexShaderStr << "attribute vec2 position;\n"
472 "varying float v_attrib;\n"
473 "void main() {\n"
474 " gl_Position = vec4(position, 0, 1);\n"
475 " v_attrib = 0.0;\n";
476
477 for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
478 {
479 vertexShaderStr << "v_attrib += attrib" << bufferIndex << ";\n";
480 }
481
482 vertexShaderStr << "}";
483
484 const std::string &fragmentShader =
485 "varying highp float v_attrib;\n"
486 "void main() {\n"
487 " gl_FragColor = vec4(v_attrib, 0, 0, 1);\n"
488 "}";
489
490 ANGLE_GL_PROGRAM(program, vertexShaderStr.str(), fragmentShader);
491 glUseProgram(program.get());
492
493 std::vector<GLfloat> data(numItems, 1.0f);
494
495 for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
496 {
497 glBindBuffer(GL_ARRAY_BUFFER, buffers[bufferIndex].get());
498 glBufferData(GL_ARRAY_BUFFER, numItems * sizeof(float), &data[0], GL_DYNAMIC_DRAW);
499
500 std::stringstream attribNameStr;
501 attribNameStr << "attrib" << bufferIndex;
502
503 GLint attribLocation = glGetAttribLocation(program.get(), attribNameStr.str().c_str());
504 ASSERT_NE(-1, attribLocation);
505
506 glVertexAttribPointer(attribLocation, 1, GL_FLOAT, GL_FALSE, 4, nullptr);
507 glEnableVertexAttribArray(attribLocation);
508 }
509
510 GLint positionLocation = glGetAttribLocation(program.get(), "position");
511 ASSERT_NE(-1, positionLocation);
512 glDisableVertexAttribArray(positionLocation);
513 glVertexAttrib2f(positionLocation, 1.0f, 1.0f);
514
515 EXPECT_GL_NO_ERROR();
516 glDrawArrays(GL_TRIANGLES, 0, numItems);
517 EXPECT_GL_ERROR(GL_OUT_OF_MEMORY);
518 }
519
520 // Tests a security bug in our CopyBufferSubData validation (integer overflow).
TEST_P(BufferDataOverflowTest,CopySubDataValidation)521 TEST_P(BufferDataOverflowTest, CopySubDataValidation)
522 {
523 GLBuffer readBuffer, writeBuffer;
524
525 glBindBuffer(GL_COPY_READ_BUFFER, readBuffer.get());
526 glBindBuffer(GL_COPY_WRITE_BUFFER, writeBuffer.get());
527
528 constexpr int bufSize = 100;
529
530 glBufferData(GL_COPY_READ_BUFFER, bufSize, nullptr, GL_STATIC_DRAW);
531 glBufferData(GL_COPY_WRITE_BUFFER, bufSize, nullptr, GL_STATIC_DRAW);
532
533 GLintptr big = std::numeric_limits<GLintptr>::max() - bufSize + 90;
534
535 glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, big, 0, 50);
536 EXPECT_GL_ERROR(GL_INVALID_VALUE);
537
538 glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, big, 50);
539 EXPECT_GL_ERROR(GL_INVALID_VALUE);
540 }
541
542 ANGLE_INSTANTIATE_TEST(BufferDataOverflowTest, ES3_D3D11());
543
544 #endif // _WIN64
545