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 
9 using namespace angle;
10 
11 namespace
12 {
13 
TypeStride(GLenum attribType)14 GLsizei TypeStride(GLenum attribType)
15 {
16     switch (attribType)
17     {
18         case GL_UNSIGNED_BYTE:
19         case GL_BYTE:
20             return 1;
21         case GL_UNSIGNED_SHORT:
22         case GL_SHORT:
23             return 2;
24         case GL_UNSIGNED_INT:
25         case GL_INT:
26         case GL_FLOAT:
27             return 4;
28         default:
29             UNREACHABLE();
30             return 0;
31     }
32 }
33 
34 template <typename T>
Normalize(T value)35 GLfloat Normalize(T value)
36 {
37     static_assert(std::is_integral<T>::value, "Integer required.");
38     if (std::is_signed<T>::value)
39     {
40         typedef typename std::make_unsigned<T>::type unsigned_type;
41         return (2.0f * static_cast<GLfloat>(value) + 1.0f) /
42                static_cast<GLfloat>(std::numeric_limits<unsigned_type>::max());
43     }
44     else
45     {
46         return static_cast<GLfloat>(value) / static_cast<GLfloat>(std::numeric_limits<T>::max());
47     }
48 }
49 
50 class VertexAttributeTest : public ANGLETest
51 {
52   protected:
VertexAttributeTest()53     VertexAttributeTest()
54         : mProgram(0), mTestAttrib(-1), mExpectedAttrib(-1), mBuffer(0), mQuadBuffer(0)
55     {
56         setWindowWidth(128);
57         setWindowHeight(128);
58         setConfigRedBits(8);
59         setConfigGreenBits(8);
60         setConfigBlueBits(8);
61         setConfigAlphaBits(8);
62         setConfigDepthBits(24);
63     }
64 
65     enum class Source
66     {
67         BUFFER,
68         IMMEDIATE,
69     };
70 
71     struct TestData final : angle::NonCopyable
72     {
TestData__anon93bce5600111::VertexAttributeTest::TestData73         TestData(GLenum typeIn,
74                  GLboolean normalizedIn,
75                  Source sourceIn,
76                  const void *inputDataIn,
77                  const GLfloat *expectedDataIn)
78             : type(typeIn),
79               normalized(normalizedIn),
80               bufferOffset(0),
81               source(sourceIn),
82               inputData(inputDataIn),
83               expectedData(expectedDataIn)
84         {
85         }
86 
87         GLenum type;
88         GLboolean normalized;
89         size_t bufferOffset;
90         Source source;
91 
92         const void *inputData;
93         const GLfloat *expectedData;
94     };
95 
setupTest(const TestData & test,GLint typeSize)96     void setupTest(const TestData &test, GLint typeSize)
97     {
98         if (mProgram == 0)
99         {
100             initBasicProgram();
101         }
102 
103         if (test.source == Source::BUFFER)
104         {
105             GLsizei dataSize = mVertexCount * TypeStride(test.type);
106             glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
107             glBufferData(GL_ARRAY_BUFFER, dataSize, test.inputData, GL_STATIC_DRAW);
108             glVertexAttribPointer(mTestAttrib, typeSize, test.type, test.normalized, 0,
109                                   reinterpret_cast<GLvoid *>(test.bufferOffset));
110             glBindBuffer(GL_ARRAY_BUFFER, 0);
111         }
112         else
113         {
114             ASSERT_EQ(Source::IMMEDIATE, test.source);
115             glBindBuffer(GL_ARRAY_BUFFER, 0);
116             glVertexAttribPointer(mTestAttrib, typeSize, test.type, test.normalized, 0,
117                                   test.inputData);
118         }
119 
120         glVertexAttribPointer(mExpectedAttrib, typeSize, GL_FLOAT, GL_FALSE, 0, test.expectedData);
121 
122         glEnableVertexAttribArray(mTestAttrib);
123         glEnableVertexAttribArray(mExpectedAttrib);
124     }
125 
checkPixels()126     void checkPixels()
127     {
128         GLint viewportSize[4];
129         glGetIntegerv(GL_VIEWPORT, viewportSize);
130 
131         GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
132         GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;
133 
134         // We need to offset our checks from triangle edges to ensure we don't fall on a single tri
135         // Avoid making assumptions of drawQuad with four checks to check the four possible tri
136         // regions
137         EXPECT_PIXEL_EQ((midPixelX + viewportSize[0]) / 2, midPixelY, 255, 255, 255, 255);
138         EXPECT_PIXEL_EQ((midPixelX + viewportSize[2]) / 2, midPixelY, 255, 255, 255, 255);
139         EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[1]) / 2, 255, 255, 255, 255);
140         EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[3]) / 2, 255, 255, 255, 255);
141     }
142 
runTest(const TestData & test)143     void runTest(const TestData &test)
144     {
145         // TODO(geofflang): Figure out why this is broken on AMD OpenGL
146         if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
147         {
148             std::cout << "Test skipped on AMD OpenGL." << std::endl;
149             return;
150         }
151 
152         for (GLint i = 0; i < 4; i++)
153         {
154             GLint typeSize = i + 1;
155             setupTest(test, typeSize);
156 
157             drawQuad(mProgram, "position", 0.5f);
158 
159             glDisableVertexAttribArray(mTestAttrib);
160             glDisableVertexAttribArray(mExpectedAttrib);
161 
162             checkPixels();
163         }
164     }
165 
SetUp()166     void SetUp() override
167     {
168         ANGLETest::SetUp();
169 
170         glClearColor(0, 0, 0, 0);
171         glClearDepthf(0.0);
172         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
173 
174         glDisable(GL_DEPTH_TEST);
175 
176         glGenBuffers(1, &mBuffer);
177     }
178 
TearDown()179     void TearDown() override
180     {
181         glDeleteProgram(mProgram);
182         glDeleteBuffers(1, &mBuffer);
183         glDeleteBuffers(1, &mQuadBuffer);
184 
185         ANGLETest::TearDown();
186     }
187 
compileMultiAttribProgram(GLint attribCount)188     GLuint compileMultiAttribProgram(GLint attribCount)
189     {
190         std::stringstream shaderStream;
191 
192         shaderStream << "attribute mediump vec4 position;" << std::endl;
193         for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex)
194         {
195             shaderStream << "attribute float a" << attribIndex << ";" << std::endl;
196         }
197         shaderStream << "varying mediump float color;" << std::endl
198                      << "void main() {" << std::endl
199                      << "  gl_Position = position;" << std::endl
200                      << "  color = 0.0;" << std::endl;
201         for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex)
202         {
203             shaderStream << "  color += a" << attribIndex << ";" << std::endl;
204         }
205         shaderStream << "}" << std::endl;
206 
207         const std::string testFragmentShaderSource =
208             SHADER_SOURCE(varying mediump float color; void main(void)
209                           {
210                               gl_FragColor = vec4(color, 0.0, 0.0, 1.0);
211                           });
212 
213         return CompileProgram(shaderStream.str(), testFragmentShaderSource);
214     }
215 
setupMultiAttribs(GLuint program,GLint attribCount,GLfloat value)216     void setupMultiAttribs(GLuint program, GLint attribCount, GLfloat value)
217     {
218         glUseProgram(program);
219         for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex)
220         {
221             std::stringstream attribStream;
222             attribStream << "a" << attribIndex;
223             GLint location = glGetAttribLocation(program, attribStream.str().c_str());
224             ASSERT_NE(-1, location);
225             glVertexAttrib1f(location, value);
226             glDisableVertexAttribArray(location);
227         }
228     }
229 
initBasicProgram()230     void initBasicProgram()
231     {
232         const std::string testVertexShaderSource =
233             "attribute mediump vec4 position;\n"
234             "attribute mediump vec4 test;\n"
235             "attribute mediump vec4 expected;\n"
236             "varying mediump vec4 color;\n"
237             "void main(void)\n"
238             "{\n"
239             "    gl_Position = position;\n"
240             "    vec4 threshold = max(abs(expected) * 0.01, 1.0 / 64.0);\n"
241             "    color = vec4(lessThanEqual(abs(test - expected), threshold));\n"
242             "}\n";
243 
244         const std::string testFragmentShaderSource =
245             "varying mediump vec4 color;\n"
246             "void main(void)\n"
247             "{\n"
248             "    gl_FragColor = color;\n"
249             "}\n";
250 
251         mProgram = CompileProgram(testVertexShaderSource, testFragmentShaderSource);
252         ASSERT_NE(0u, mProgram);
253 
254         mTestAttrib = glGetAttribLocation(mProgram, "test");
255         ASSERT_NE(-1, mTestAttrib);
256         mExpectedAttrib = glGetAttribLocation(mProgram, "expected");
257         ASSERT_NE(-1, mExpectedAttrib);
258 
259         glUseProgram(mProgram);
260     }
261 
262     static const size_t mVertexCount = 24;
263 
264     GLuint mProgram;
265     GLint mTestAttrib;
266     GLint mExpectedAttrib;
267     GLuint mBuffer;
268     GLuint mQuadBuffer;
269 };
270 
TEST_P(VertexAttributeTest,UnsignedByteUnnormalized)271 TEST_P(VertexAttributeTest, UnsignedByteUnnormalized)
272 {
273     GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 };
274     GLfloat expectedData[mVertexCount];
275     for (size_t i = 0; i < mVertexCount; i++)
276     {
277         expectedData[i] = inputData[i];
278     }
279 
280     TestData data(GL_UNSIGNED_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
281     runTest(data);
282 }
283 
TEST_P(VertexAttributeTest,UnsignedByteNormalized)284 TEST_P(VertexAttributeTest, UnsignedByteNormalized)
285 {
286     GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 };
287     GLfloat expectedData[mVertexCount];
288     for (size_t i = 0; i < mVertexCount; i++)
289     {
290         expectedData[i] = Normalize(inputData[i]);
291     }
292 
293     TestData data(GL_UNSIGNED_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
294     runTest(data);
295 }
296 
TEST_P(VertexAttributeTest,ByteUnnormalized)297 TEST_P(VertexAttributeTest, ByteUnnormalized)
298 {
299     GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 };
300     GLfloat expectedData[mVertexCount];
301     for (size_t i = 0; i < mVertexCount; i++)
302     {
303         expectedData[i] = inputData[i];
304     }
305 
306     TestData data(GL_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
307     runTest(data);
308 }
309 
TEST_P(VertexAttributeTest,ByteNormalized)310 TEST_P(VertexAttributeTest, ByteNormalized)
311 {
312     GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 };
313     GLfloat expectedData[mVertexCount];
314     for (size_t i = 0; i < mVertexCount; i++)
315     {
316         expectedData[i] = Normalize(inputData[i]);
317     }
318 
319     TestData data(GL_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
320     runTest(data);
321 }
322 
TEST_P(VertexAttributeTest,UnsignedShortUnnormalized)323 TEST_P(VertexAttributeTest, UnsignedShortUnnormalized)
324 {
325     GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 };
326     GLfloat expectedData[mVertexCount];
327     for (size_t i = 0; i < mVertexCount; i++)
328     {
329         expectedData[i] = inputData[i];
330     }
331 
332     TestData data(GL_UNSIGNED_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
333     runTest(data);
334 }
335 
TEST_P(VertexAttributeTest,UnsignedShortNormalized)336 TEST_P(VertexAttributeTest, UnsignedShortNormalized)
337 {
338     GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 };
339     GLfloat expectedData[mVertexCount];
340     for (size_t i = 0; i < mVertexCount; i++)
341     {
342         expectedData[i] = Normalize(inputData[i]);
343     }
344 
345     TestData data(GL_UNSIGNED_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
346     runTest(data);
347 }
348 
TEST_P(VertexAttributeTest,ShortUnnormalized)349 TEST_P(VertexAttributeTest, ShortUnnormalized)
350 {
351     GLshort inputData[mVertexCount] = {  0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 };
352     GLfloat expectedData[mVertexCount];
353     for (size_t i = 0; i < mVertexCount; i++)
354     {
355         expectedData[i] = inputData[i];
356     }
357 
358     TestData data(GL_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
359     runTest(data);
360 }
361 
TEST_P(VertexAttributeTest,ShortNormalized)362 TEST_P(VertexAttributeTest, ShortNormalized)
363 {
364     GLshort inputData[mVertexCount] = {  0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 };
365     GLfloat expectedData[mVertexCount];
366     for (size_t i = 0; i < mVertexCount; i++)
367     {
368         expectedData[i] = Normalize(inputData[i]);
369     }
370 
371     TestData data(GL_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
372     runTest(data);
373 }
374 
375 class VertexAttributeTestES3 : public VertexAttributeTest
376 {
377   protected:
VertexAttributeTestES3()378     VertexAttributeTestES3() {}
379 };
380 
TEST_P(VertexAttributeTestES3,IntUnnormalized)381 TEST_P(VertexAttributeTestES3, IntUnnormalized)
382 {
383     GLint lo                      = std::numeric_limits<GLint>::min();
384     GLint hi                      = std::numeric_limits<GLint>::max();
385     GLint inputData[mVertexCount] = {0, 1, 2, 3, -1, -2, -3, -4, -1, hi, hi - 1, lo, lo + 1};
386     GLfloat expectedData[mVertexCount];
387     for (size_t i = 0; i < mVertexCount; i++)
388     {
389         expectedData[i] = static_cast<GLfloat>(inputData[i]);
390     }
391 
392     TestData data(GL_INT, GL_FALSE, Source::BUFFER, inputData, expectedData);
393     runTest(data);
394 }
395 
TEST_P(VertexAttributeTestES3,IntNormalized)396 TEST_P(VertexAttributeTestES3, IntNormalized)
397 {
398     GLint lo                      = std::numeric_limits<GLint>::min();
399     GLint hi                      = std::numeric_limits<GLint>::max();
400     GLint inputData[mVertexCount] = {0, 1, 2, 3, -1, -2, -3, -4, -1, hi, hi - 1, lo, lo + 1};
401     GLfloat expectedData[mVertexCount];
402     for (size_t i = 0; i < mVertexCount; i++)
403     {
404         expectedData[i] = Normalize(inputData[i]);
405     }
406 
407     TestData data(GL_INT, GL_TRUE, Source::BUFFER, inputData, expectedData);
408     runTest(data);
409 }
410 
TEST_P(VertexAttributeTestES3,UnsignedIntUnnormalized)411 TEST_P(VertexAttributeTestES3, UnsignedIntUnnormalized)
412 {
413     GLuint mid                     = std::numeric_limits<GLuint>::max() >> 1;
414     GLuint hi                      = std::numeric_limits<GLuint>::max();
415     GLuint inputData[mVertexCount] = {0,       1,   2,       3,      254,    255, 256,
416                                       mid - 1, mid, mid + 1, hi - 2, hi - 1, hi};
417     GLfloat expectedData[mVertexCount];
418     for (size_t i = 0; i < mVertexCount; i++)
419     {
420         expectedData[i] = static_cast<GLfloat>(inputData[i]);
421     }
422 
423     TestData data(GL_UNSIGNED_INT, GL_FALSE, Source::BUFFER, inputData, expectedData);
424     runTest(data);
425 }
426 
TEST_P(VertexAttributeTestES3,UnsignedIntNormalized)427 TEST_P(VertexAttributeTestES3, UnsignedIntNormalized)
428 {
429     GLuint mid                     = std::numeric_limits<GLuint>::max() >> 1;
430     GLuint hi                      = std::numeric_limits<GLuint>::max();
431     GLuint inputData[mVertexCount] = {0,       1,   2,       3,      254,    255, 256,
432                                       mid - 1, mid, mid + 1, hi - 2, hi - 1, hi};
433     GLfloat expectedData[mVertexCount];
434     for (size_t i = 0; i < mVertexCount; i++)
435     {
436         expectedData[i] = Normalize(inputData[i]);
437     }
438 
439     TestData data(GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData, expectedData);
440     runTest(data);
441 }
442 
443 // Validate that we can support GL_MAX_ATTRIBS attribs
TEST_P(VertexAttributeTest,MaxAttribs)444 TEST_P(VertexAttributeTest, MaxAttribs)
445 {
446     // TODO(jmadill): Figure out why we get this error on AMD/OpenGL and Intel.
447     if ((IsIntel() || IsAMD()) && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
448     {
449         std::cout << "Test skipped on Intel and AMD." << std::endl;
450         return;
451     }
452 
453     GLint maxAttribs;
454     glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttribs);
455     ASSERT_GL_NO_ERROR();
456 
457     // Reserve one attrib for position
458     GLint drawAttribs = maxAttribs - 1;
459 
460     GLuint program = compileMultiAttribProgram(drawAttribs);
461     ASSERT_NE(0u, program);
462 
463     setupMultiAttribs(program, drawAttribs, 0.5f / static_cast<float>(drawAttribs));
464     drawQuad(program, "position", 0.5f);
465 
466     EXPECT_GL_NO_ERROR();
467     EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1);
468 }
469 
470 // Validate that we cannot support GL_MAX_ATTRIBS+1 attribs
TEST_P(VertexAttributeTest,MaxAttribsPlusOne)471 TEST_P(VertexAttributeTest, MaxAttribsPlusOne)
472 {
473     // TODO(jmadill): Figure out why we get this error on AMD/ES2/OpenGL
474     if (IsAMD() && GetParam() == ES2_OPENGL())
475     {
476         std::cout << "Test disabled on AMD/ES2/OpenGL" << std::endl;
477         return;
478     }
479 
480     GLint maxAttribs;
481     glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttribs);
482     ASSERT_GL_NO_ERROR();
483 
484     // Exceed attrib count by one (counting position)
485     GLint drawAttribs = maxAttribs;
486 
487     GLuint program = compileMultiAttribProgram(drawAttribs);
488     ASSERT_EQ(0u, program);
489 }
490 
491 // Simple test for when we use glBindAttribLocation
TEST_P(VertexAttributeTest,SimpleBindAttribLocation)492 TEST_P(VertexAttributeTest, SimpleBindAttribLocation)
493 {
494     // TODO(jmadill): Figure out why this fails on Intel.
495     if (IsIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
496     {
497         std::cout << "Test skipped on Intel." << std::endl;
498         return;
499     }
500 
501     // Re-use the multi-attrib program, binding attribute 0
502     GLuint program = compileMultiAttribProgram(1);
503     glBindAttribLocation(program, 2, "position");
504     glBindAttribLocation(program, 3, "a0");
505     glLinkProgram(program);
506 
507     // Setup and draw the quad
508     setupMultiAttribs(program, 1, 0.5f);
509     drawQuad(program, "position", 0.5f);
510     EXPECT_GL_NO_ERROR();
511     EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1);
512 }
513 
514 // Verify that drawing with a large out-of-range offset generates INVALID_OPERATION.
TEST_P(VertexAttributeTest,DrawArraysBufferTooSmall)515 TEST_P(VertexAttributeTest, DrawArraysBufferTooSmall)
516 {
517     GLfloat inputData[mVertexCount];
518     GLfloat expectedData[mVertexCount];
519     for (size_t count = 0; count < mVertexCount; ++count)
520     {
521         inputData[count]    = static_cast<GLfloat>(count);
522         expectedData[count] = inputData[count];
523     }
524 
525     TestData data(GL_FLOAT, GL_FALSE, Source::BUFFER, inputData, expectedData);
526     data.bufferOffset = mVertexCount * TypeStride(GL_FLOAT);
527 
528     setupTest(data, 1);
529     drawQuad(mProgram, "position", 0.5f);
530     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
531 }
532 
533 // Verify that index draw with an out-of-range offset generates INVALID_OPERATION.
TEST_P(VertexAttributeTest,DrawElementsBufferTooSmall)534 TEST_P(VertexAttributeTest, DrawElementsBufferTooSmall)
535 {
536     GLfloat inputData[mVertexCount];
537     GLfloat expectedData[mVertexCount];
538     for (size_t count = 0; count < mVertexCount; ++count)
539     {
540         inputData[count]    = static_cast<GLfloat>(count);
541         expectedData[count] = inputData[count];
542     }
543 
544     TestData data(GL_FLOAT, GL_FALSE, Source::BUFFER, inputData, expectedData);
545     data.bufferOffset = (mVertexCount - 3) * TypeStride(GL_FLOAT);
546 
547     setupTest(data, 1);
548     drawIndexedQuad(mProgram, "position", 0.5f);
549     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
550 }
551 
552 // Verify that using a different start vertex doesn't mess up the draw.
TEST_P(VertexAttributeTest,DrawArraysWithBufferOffset)553 TEST_P(VertexAttributeTest, DrawArraysWithBufferOffset)
554 {
555     // TODO(jmadill): Diagnose this failure.
556     if (IsD3D11_FL93())
557     {
558         std::cout << "Test disabled on D3D11 FL 9_3" << std::endl;
559         return;
560     }
561 
562     // TODO(geofflang): Figure out why this is broken on AMD OpenGL
563     if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
564     {
565         std::cout << "Test skipped on AMD OpenGL." << std::endl;
566         return;
567     }
568 
569     initBasicProgram();
570     glUseProgram(mProgram);
571 
572     GLfloat inputData[mVertexCount];
573     GLfloat expectedData[mVertexCount];
574     for (size_t count = 0; count < mVertexCount; ++count)
575     {
576         inputData[count]    = static_cast<GLfloat>(count);
577         expectedData[count] = inputData[count];
578     }
579 
580     auto quadVertices        = GetQuadVertices();
581     GLsizei quadVerticesSize = static_cast<GLsizei>(quadVertices.size() * sizeof(quadVertices[0]));
582 
583     glGenBuffers(1, &mQuadBuffer);
584     glBindBuffer(GL_ARRAY_BUFFER, mQuadBuffer);
585     glBufferData(GL_ARRAY_BUFFER, quadVerticesSize + sizeof(Vector3), nullptr, GL_STATIC_DRAW);
586     glBufferSubData(GL_ARRAY_BUFFER, 0, quadVerticesSize, quadVertices.data());
587 
588     GLint positionLocation = glGetAttribLocation(mProgram, "position");
589     ASSERT_NE(-1, positionLocation);
590     glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
591     glEnableVertexAttribArray(positionLocation);
592 
593     GLsizei dataSize = mVertexCount * TypeStride(GL_FLOAT);
594     glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
595     glBufferData(GL_ARRAY_BUFFER, dataSize + TypeStride(GL_FLOAT), nullptr, GL_STATIC_DRAW);
596     glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, inputData);
597     glVertexAttribPointer(mTestAttrib, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
598     glEnableVertexAttribArray(mTestAttrib);
599 
600     glBindBuffer(GL_ARRAY_BUFFER, 0);
601     glVertexAttribPointer(mExpectedAttrib, 1, GL_FLOAT, GL_FALSE, 0, expectedData);
602     glEnableVertexAttribArray(mExpectedAttrib);
603 
604     // Vertex draw with no start vertex offset (second argument is zero).
605     glDrawArrays(GL_TRIANGLES, 0, 6);
606     checkPixels();
607 
608     // Draw offset by one vertex.
609     glDrawArrays(GL_TRIANGLES, 1, 6);
610     checkPixels();
611 
612     EXPECT_GL_NO_ERROR();
613 }
614 
615 class VertexAttributeCachingTest : public VertexAttributeTest
616 {
617   protected:
VertexAttributeCachingTest()618     VertexAttributeCachingTest() {}
619 
620     void SetUp() override;
621 
622     template <typename DestT>
623     static std::vector<GLfloat> GetExpectedData(const std::vector<GLubyte> &srcData,
624                                                 GLenum attribType,
625                                                 GLboolean normalized);
626 
initDoubleAttribProgram()627     void initDoubleAttribProgram()
628     {
629         const std::string testVertexShaderSource =
630             "attribute mediump vec4 position;\n"
631             "attribute mediump vec4 test;\n"
632             "attribute mediump vec4 expected;\n"
633             "attribute mediump vec4 test2;\n"
634             "attribute mediump vec4 expected2;\n"
635             "varying mediump vec4 color;\n"
636             "void main(void)\n"
637             "{\n"
638             "    gl_Position = position;\n"
639             "    vec4 threshold = max(abs(expected) * 0.01, 1.0 / 64.0);\n"
640             "    color = vec4(lessThanEqual(abs(test - expected), threshold));\n"
641             "    vec4 threshold2 = max(abs(expected2) * 0.01, 1.0 / 64.0);\n"
642             "    color += vec4(lessThanEqual(abs(test2 - expected2), threshold2));\n"
643             "}\n";
644 
645         const std::string testFragmentShaderSource =
646             "varying mediump vec4 color;\n"
647             "void main(void)\n"
648             "{\n"
649             "    gl_FragColor = color;\n"
650             "}\n";
651 
652         mProgram = CompileProgram(testVertexShaderSource, testFragmentShaderSource);
653         ASSERT_NE(0u, mProgram);
654 
655         mTestAttrib = glGetAttribLocation(mProgram, "test");
656         ASSERT_NE(-1, mTestAttrib);
657         mExpectedAttrib = glGetAttribLocation(mProgram, "expected");
658         ASSERT_NE(-1, mExpectedAttrib);
659 
660         glUseProgram(mProgram);
661     }
662 
663     struct AttribData
664     {
665         AttribData(GLenum typeIn, GLint sizeIn, GLboolean normalizedIn, GLsizei strideIn);
666 
667         GLenum type;
668         GLint size;
669         GLboolean normalized;
670         GLsizei stride;
671     };
672 
673     std::vector<AttribData> mTestData;
674     std::map<GLenum, std::vector<GLfloat>> mExpectedData;
675     std::map<GLenum, std::vector<GLfloat>> mNormExpectedData;
676 };
677 
AttribData(GLenum typeIn,GLint sizeIn,GLboolean normalizedIn,GLsizei strideIn)678 VertexAttributeCachingTest::AttribData::AttribData(GLenum typeIn,
679                                                    GLint sizeIn,
680                                                    GLboolean normalizedIn,
681                                                    GLsizei strideIn)
682     : type(typeIn), size(sizeIn), normalized(normalizedIn), stride(strideIn)
683 {
684 }
685 
686 // static
687 template <typename DestT>
GetExpectedData(const std::vector<GLubyte> & srcData,GLenum attribType,GLboolean normalized)688 std::vector<GLfloat> VertexAttributeCachingTest::GetExpectedData(
689     const std::vector<GLubyte> &srcData,
690     GLenum attribType,
691     GLboolean normalized)
692 {
693     std::vector<GLfloat> expectedData;
694 
695     const DestT *typedSrcPtr = reinterpret_cast<const DestT *>(srcData.data());
696     size_t iterations        = srcData.size() / TypeStride(attribType);
697 
698     if (normalized)
699     {
700         for (size_t index = 0; index < iterations; ++index)
701         {
702             expectedData.push_back(Normalize(typedSrcPtr[index]));
703         }
704     }
705     else
706     {
707         for (size_t index = 0; index < iterations; ++index)
708         {
709             expectedData.push_back(static_cast<GLfloat>(typedSrcPtr[index]));
710         }
711     }
712 
713     return expectedData;
714 }
715 
SetUp()716 void VertexAttributeCachingTest::SetUp()
717 {
718     VertexAttributeTest::SetUp();
719 
720     glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
721 
722     std::vector<GLubyte> srcData;
723     for (size_t count = 0; count < 4; ++count)
724     {
725         for (GLubyte i = 0; i < std::numeric_limits<GLubyte>::max(); ++i)
726         {
727             srcData.push_back(i);
728         }
729     }
730 
731     glBufferData(GL_ARRAY_BUFFER, srcData.size(), srcData.data(), GL_STATIC_DRAW);
732 
733     GLint viewportSize[4];
734     glGetIntegerv(GL_VIEWPORT, viewportSize);
735 
736     std::vector<GLenum> attribTypes;
737     attribTypes.push_back(GL_BYTE);
738     attribTypes.push_back(GL_UNSIGNED_BYTE);
739     attribTypes.push_back(GL_SHORT);
740     attribTypes.push_back(GL_UNSIGNED_SHORT);
741 
742     if (getClientMajorVersion() >= 3)
743     {
744         attribTypes.push_back(GL_INT);
745         attribTypes.push_back(GL_UNSIGNED_INT);
746     }
747 
748     const GLint maxSize     = 4;
749     const GLsizei maxStride = 4;
750 
751     for (GLenum attribType : attribTypes)
752     {
753         for (GLint attribSize = 1; attribSize <= maxSize; ++attribSize)
754         {
755             for (GLsizei stride = 1; stride <= maxStride; ++stride)
756             {
757                 mTestData.push_back(AttribData(attribType, attribSize, GL_FALSE, stride));
758                 if (attribType != GL_FLOAT)
759                 {
760                     mTestData.push_back(AttribData(attribType, attribSize, GL_TRUE, stride));
761                 }
762             }
763         }
764     }
765 
766     mExpectedData[GL_BYTE]          = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_FALSE);
767     mExpectedData[GL_UNSIGNED_BYTE] = GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_FALSE);
768     mExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_FALSE);
769     mExpectedData[GL_UNSIGNED_SHORT] =
770         GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_FALSE);
771     mExpectedData[GL_INT]          = GetExpectedData<GLint>(srcData, GL_INT, GL_FALSE);
772     mExpectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_FALSE);
773 
774     mNormExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_TRUE);
775     mNormExpectedData[GL_UNSIGNED_BYTE] =
776         GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_TRUE);
777     mNormExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_TRUE);
778     mNormExpectedData[GL_UNSIGNED_SHORT] =
779         GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_TRUE);
780     mNormExpectedData[GL_INT]          = GetExpectedData<GLint>(srcData, GL_INT, GL_TRUE);
781     mNormExpectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_TRUE);
782 }
783 
784 // In D3D11, we must sometimes translate buffer data into static attribute caches. We also use a
785 // cache management scheme which garbage collects old attributes after we start using too much
786 // cache data. This test tries to make as many attribute caches from a single buffer as possible
787 // to stress-test the caching code.
TEST_P(VertexAttributeCachingTest,BufferMulticaching)788 TEST_P(VertexAttributeCachingTest, BufferMulticaching)
789 {
790     if (IsAMD() && IsDesktopOpenGL())
791     {
792         std::cout << "Test skipped on AMD OpenGL." << std::endl;
793         return;
794     }
795 
796     initBasicProgram();
797 
798     glEnableVertexAttribArray(mTestAttrib);
799     glEnableVertexAttribArray(mExpectedAttrib);
800 
801     ASSERT_GL_NO_ERROR();
802 
803     for (const auto &data : mTestData)
804     {
805         const auto &expected =
806             (data.normalized) ? mNormExpectedData[data.type] : mExpectedData[data.type];
807 
808         GLsizei baseStride = static_cast<GLsizei>(data.size) * data.stride;
809         GLsizei stride     = TypeStride(data.type) * baseStride;
810 
811         glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
812         glVertexAttribPointer(mTestAttrib, data.size, data.type, data.normalized, stride, nullptr);
813         glBindBuffer(GL_ARRAY_BUFFER, 0);
814         glVertexAttribPointer(mExpectedAttrib, data.size, GL_FLOAT, GL_FALSE,
815                               sizeof(GLfloat) * baseStride, expected.data());
816         drawQuad(mProgram, "position", 0.5f);
817         ASSERT_GL_NO_ERROR();
818         EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255);
819     }
820 }
821 
822 // With D3D11 dirty bits for VertxArray11, we can leave vertex state unchanged if there aren't any
823 // GL calls that affect it. This test targets leaving one vertex attribute unchanged between draw
824 // calls while changing another vertex attribute enough that it clears the static buffer cache
825 // after enough iterations. It validates the unchanged attributes don't get deleted incidentally.
TEST_P(VertexAttributeCachingTest,BufferMulticachingWithOneUnchangedAttrib)826 TEST_P(VertexAttributeCachingTest, BufferMulticachingWithOneUnchangedAttrib)
827 {
828     if (IsAMD() && IsDesktopOpenGL())
829     {
830         std::cout << "Test skipped on AMD OpenGL." << std::endl;
831         return;
832     }
833 
834     initDoubleAttribProgram();
835 
836     GLint testAttrib2Location = glGetAttribLocation(mProgram, "test2");
837     ASSERT_NE(-1, testAttrib2Location);
838     GLint expectedAttrib2Location = glGetAttribLocation(mProgram, "expected2");
839     ASSERT_NE(-1, expectedAttrib2Location);
840 
841     glEnableVertexAttribArray(mTestAttrib);
842     glEnableVertexAttribArray(mExpectedAttrib);
843     glEnableVertexAttribArray(testAttrib2Location);
844     glEnableVertexAttribArray(expectedAttrib2Location);
845 
846     ASSERT_GL_NO_ERROR();
847 
848     // Use an attribute that we know must be converted. This is a bit sensitive.
849     glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
850     glVertexAttribPointer(testAttrib2Location, 3, GL_UNSIGNED_SHORT, GL_FALSE, 6, nullptr);
851     glBindBuffer(GL_ARRAY_BUFFER, 0);
852     glVertexAttribPointer(expectedAttrib2Location, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3,
853                           mExpectedData[GL_UNSIGNED_SHORT].data());
854 
855     for (const auto &data : mTestData)
856     {
857         const auto &expected =
858             (data.normalized) ? mNormExpectedData[data.type] : mExpectedData[data.type];
859 
860         GLsizei baseStride = static_cast<GLsizei>(data.size) * data.stride;
861         GLsizei stride     = TypeStride(data.type) * baseStride;
862 
863         glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
864         glVertexAttribPointer(mTestAttrib, data.size, data.type, data.normalized, stride, nullptr);
865         glBindBuffer(GL_ARRAY_BUFFER, 0);
866         glVertexAttribPointer(mExpectedAttrib, data.size, GL_FLOAT, GL_FALSE,
867                               sizeof(GLfloat) * baseStride, expected.data());
868         drawQuad(mProgram, "position", 0.5f);
869 
870         ASSERT_GL_NO_ERROR();
871         EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255);
872     }
873 }
874 
875 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
876 // D3D11 Feature Level 9_3 uses different D3D formats for vertex attribs compared to Feature Levels 10_0+, so we should test them separately.
877 ANGLE_INSTANTIATE_TEST(VertexAttributeTest,
878                        ES2_D3D9(),
879                        ES2_D3D11(),
880                        ES2_D3D11_FL9_3(),
881                        ES2_OPENGL(),
882                        ES3_OPENGL(),
883                        ES2_OPENGLES(),
884                        ES3_OPENGLES());
885 
886 ANGLE_INSTANTIATE_TEST(VertexAttributeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
887 
888 ANGLE_INSTANTIATE_TEST(VertexAttributeCachingTest,
889                        ES2_D3D9(),
890                        ES2_D3D11(),
891                        ES3_D3D11(),
892                        ES3_OPENGL());
893 
894 }  // anonymous namespace
895