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