xref: /aosp_15_r20/external/angle/src/tests/gl_tests/AttributeLayoutTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2018 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 // AttributeLayoutTest:
7 //   Test various layouts of vertex attribute data:
8 //   - in memory, in buffer object, or combination of both
9 //   - sequential or interleaved
10 //   - various combinations of data types
11 
12 #include <vector>
13 
14 #include "test_utils/ANGLETest.h"
15 #include "test_utils/gl_raii.h"
16 
17 using namespace angle;
18 
19 namespace
20 {
21 
22 // Test will draw these four triangles.
23 // clang-format off
24 constexpr double kTriangleData[] = {
25     // xy       rgb
26     0,0,        1,1,0,
27     -1,+1,      1,1,0,
28     +1,+1,      1,1,0,
29 
30     0,0,        0,1,0,
31     +1,+1,      0,1,0,
32     +1,-1,      0,1,0,
33 
34     0,0,        0,1,1,
35     +1,-1,      0,1,1,
36     -1,-1,      0,1,1,
37 
38     0,0,        1,0,1,
39     -1,-1,      1,0,1,
40     -1,+1,      1,0,1,
41 };
42 // clang-format on
43 
44 constexpr size_t kNumVertices = ArraySize(kTriangleData) / 5;
45 
46 // Vertex data source description.
47 class VertexData
48 {
49   public:
VertexData(int dimension,const double * data,unsigned offset,unsigned stride,unsigned numVertices)50     VertexData(int dimension,
51                const double *data,
52                unsigned offset,
53                unsigned stride,
54                unsigned numVertices)
55         : mNumVertices(numVertices),
56           mDimension(dimension),
57           mData(data),
58           mOffset(offset),
59           mStride(stride)
60     {}
getDimension() const61     int getDimension() const { return mDimension; }
getNumVertices() const62     unsigned getNumVertices() const { return mNumVertices; }
getValue(unsigned vertexNumber,int component) const63     double getValue(unsigned vertexNumber, int component) const
64     {
65         return mData[mOffset + mStride * vertexNumber + component];
66     }
67 
68   private:
69     unsigned mNumVertices;
70     int mDimension;
71     const double *mData;
72     // offset and stride in doubles
73     unsigned mOffset;
74     unsigned mStride;
75 };
76 
77 // A container for one or more vertex attributes.
78 class Container
79 {
80   public:
81     static constexpr size_t kSize = 1024;
82 
open(void)83     void open(void) { memset(mMemory, 0xff, kSize); }
getDestination(size_t offset)84     void *getDestination(size_t offset) { return mMemory + offset; }
close(void)85     virtual void close(void) {}
~Container()86     virtual ~Container() {}
87     virtual const char *getAddress() = 0;
88     virtual GLuint getBuffer()       = 0;
89 
90   protected:
91     char mMemory[kSize];
92 };
93 
94 // Vertex attribute data in client memory.
95 class Memory : public Container
96 {
97   public:
getAddress()98     const char *getAddress() override { return mMemory; }
getBuffer()99     GLuint getBuffer() override { return 0; }
100 };
101 
102 // Vertex attribute data in buffer object.
103 class Buffer : public Container
104 {
105   public:
close(void)106     void close(void) override
107     {
108         glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
109         glBufferData(GL_ARRAY_BUFFER, sizeof(mMemory), mMemory, GL_STATIC_DRAW);
110     }
111 
getAddress()112     const char *getAddress() override { return nullptr; }
getBuffer()113     GLuint getBuffer() override { return mBuffer; }
114 
115   protected:
116     GLBuffer mBuffer;
117 };
118 
119 // Encapsulate the storage, layout, format and data of a vertex attribute.
120 struct Attrib
121 {
openContainer__anon4c90fb2d0111::Attrib122     void openContainer(void) const { mContainer->open(); }
123 
fillContainer__anon4c90fb2d0111::Attrib124     void fillContainer(void) const
125     {
126         for (unsigned i = 0; i < mData.getNumVertices(); ++i)
127         {
128             for (int j = 0; j < mData.getDimension(); ++j)
129             {
130                 size_t destOffset = mOffset + mStride * i + mCTypeSize * j;
131                 if (destOffset + mCTypeSize > Container::kSize)
132                     FAIL() << "test case does not fit container";
133 
134                 double value = mData.getValue(i, j);
135                 if (mGLType == GL_FIXED)
136                     value *= 1 << 16;
137                 else if (mNormalized)
138                 {
139                     if (value < mMinIn || value > mMaxIn)
140                         FAIL() << "test data does not fit format";
141                     value = (value - mMinIn) * mScale + mMinOut;
142                 }
143 
144                 mStore(value, mContainer->getDestination(destOffset));
145             }
146         }
147     }
148 
closeContainer__anon4c90fb2d0111::Attrib149     void closeContainer(void) const { mContainer->close(); }
150 
enable__anon4c90fb2d0111::Attrib151     void enable(unsigned index) const
152     {
153         glBindBuffer(GL_ARRAY_BUFFER, mContainer->getBuffer());
154         if (mPureInteger)
155         {
156             glVertexAttribIPointer(index, mData.getDimension(), mGLType, mStride,
157                                    mContainer->getAddress() + mOffset);
158         }
159         else
160         {
161             glVertexAttribPointer(index, mData.getDimension(), mGLType, mNormalized, mStride,
162                                   mContainer->getAddress() + mOffset);
163         }
164         EXPECT_GL_NO_ERROR();
165         glEnableVertexAttribArray(index);
166     }
167 
inClientMemory__anon4c90fb2d0111::Attrib168     bool inClientMemory(void) const { return mContainer->getAddress() != nullptr; }
169 
170     std::shared_ptr<Container> mContainer;
171     unsigned mOffset;
172     unsigned mStride;
173     const VertexData &mData;
174     void (*mStore)(double value, void *dest);
175     GLenum mGLType;
176     GLboolean mNormalized;
177     GLboolean mPureInteger = GL_FALSE;
178     size_t mCTypeSize;
179     double mMinIn;
180     double mMaxIn;
181     double mMinOut;
182     double mScale;
183 };
184 
185 // Change type and store.
186 template <class T>
Store(double value,void * dest)187 void Store(double value, void *dest)
188 {
189     T v = static_cast<T>(value);
190     memcpy(dest, &v, sizeof(v));
191 }
192 
193 // Function object that makes Attrib structs according to a vertex format.
194 template <class CType, GLenum GLType, bool Normalized, bool PureInteger = false>
195 class Format
196 {
197     static_assert(!(Normalized && GLType == GL_FLOAT), "Normalized float does not make sense.");
198 
199   public:
Format(bool es3)200     Format(bool es3) : mES3(es3) {}
201 
operator ()(std::shared_ptr<Container> container,unsigned offset,unsigned stride,const VertexData & data) const202     Attrib operator()(std::shared_ptr<Container> container,
203                       unsigned offset,
204                       unsigned stride,
205                       const VertexData &data) const
206     {
207         double minIn    = 0;
208         double maxIn    = 1;
209         double minOut   = std::numeric_limits<CType>::min();
210         double rangeOut = std::numeric_limits<CType>::max() - minOut;
211 
212         if (std::is_signed<CType>::value)
213         {
214             minIn = -1;
215             maxIn = +1;
216             if (mES3)
217             {
218                 minOut += 1;
219                 rangeOut -= 1;
220             }
221         }
222 
223         return {
224             container,
225             offset,
226             stride,
227             data,
228             Store<CType>,
229             GLType,
230             Normalized,
231             PureInteger,
232             sizeof(CType),
233             minIn,
234             maxIn,
235             minOut,
236             rangeOut / (maxIn - minIn),
237         };
238     }
239 
240   protected:
241     const bool mES3;
242 };
243 
244 typedef std::vector<Attrib> TestCase;
245 
PrepareTestCase(const TestCase & tc)246 void PrepareTestCase(const TestCase &tc)
247 {
248     for (const Attrib &a : tc)
249         a.openContainer();
250     for (const Attrib &a : tc)
251         a.fillContainer();
252     for (const Attrib &a : tc)
253         a.closeContainer();
254     unsigned i = 0;
255     for (const Attrib &a : tc)
256         a.enable(i++);
257 }
258 
259 class AttributeLayoutTest : public ANGLETest<>
260 {
261   protected:
AttributeLayoutTest()262     AttributeLayoutTest()
263         : mProgram(0),
264           mCoord(2, kTriangleData, 0, 5, kNumVertices),
265           mColor(3, kTriangleData, 2, 5, kNumVertices)
266     {
267         setWindowWidth(128);
268         setWindowHeight(128);
269         setConfigRedBits(8);
270         setConfigGreenBits(8);
271         setConfigBlueBits(8);
272         setConfigAlphaBits(8);
273     }
274 
275     void GetTestCases(void);
276 
testSetUp()277     void testSetUp() override
278     {
279         glClearColor(.2f, .2f, .2f, .0f);
280         glClear(GL_COLOR_BUFFER_BIT);
281 
282         glDisable(GL_DEPTH_TEST);
283 
284         constexpr char kVS[] =
285             "attribute mediump vec2 coord;\n"
286             "attribute mediump vec3 color;\n"
287             "varying mediump vec3 vcolor;\n"
288             "void main(void)\n"
289             "{\n"
290             "    gl_Position = vec4(coord, 0, 1);\n"
291             "    vcolor = color;\n"
292             "}\n";
293 
294         constexpr char kFS[] =
295             "varying mediump vec3 vcolor;\n"
296             "void main(void)\n"
297             "{\n"
298             "    gl_FragColor = vec4(vcolor, 0);\n"
299             "}\n";
300 
301         mProgram = CompileProgram(kVS, kFS);
302         ASSERT_NE(0u, mProgram);
303         glUseProgram(mProgram);
304 
305         glGenBuffers(1, &mIndexBuffer);
306 
307         GetTestCases();
308     }
309 
testTearDown()310     void testTearDown() override
311     {
312         mTestCases.clear();
313         glDeleteProgram(mProgram);
314         glDeleteBuffers(1, &mIndexBuffer);
315     }
316 
Skip(const TestCase &)317     virtual bool Skip(const TestCase &) { return false; }
318     virtual void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) = 0;
319 
Run(bool drawFirstTriangle)320     void Run(bool drawFirstTriangle)
321     {
322         glViewport(0, 0, getWindowWidth(), getWindowHeight());
323         glUseProgram(mProgram);
324 
325         for (unsigned i = 0; i < mTestCases.size(); ++i)
326         {
327             if (mTestCases[i].size() == 0 || Skip(mTestCases[i]))
328                 continue;
329 
330             PrepareTestCase(mTestCases[i]);
331 
332             glClear(GL_COLOR_BUFFER_BIT);
333 
334             std::string testCase;
335             if (drawFirstTriangle)
336             {
337                 Draw(0, kNumVertices, mIndices);
338                 testCase = "draw";
339             }
340             else
341             {
342                 Draw(3, kNumVertices - 3, mIndices + 3);
343                 testCase = "skip";
344             }
345 
346             testCase += " first triangle case ";
347             int w = getWindowWidth() / 4;
348             int h = getWindowHeight() / 4;
349             if (drawFirstTriangle)
350             {
351                 EXPECT_PIXEL_EQ(w * 2, h * 3, 255, 255, 0, 0) << testCase << i;
352             }
353             else
354             {
355                 EXPECT_PIXEL_EQ(w * 2, h * 3, 51, 51, 51, 0) << testCase << i;
356             }
357             EXPECT_PIXEL_EQ(w * 3, h * 2, 0, 255, 0, 0) << testCase << i;
358             EXPECT_PIXEL_EQ(w * 2, h * 1, 0, 255, 255, 0) << testCase << i;
359             EXPECT_PIXEL_EQ(w * 1, h * 2, 255, 0, 255, 0) << testCase << i;
360         }
361     }
362 
363     static const GLushort mIndices[kNumVertices];
364 
365     GLuint mProgram;
366     GLuint mIndexBuffer;
367 
368     std::vector<TestCase> mTestCases;
369 
370     VertexData mCoord;
371     VertexData mColor;
372 };
373 const GLushort AttributeLayoutTest::mIndices[kNumVertices] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
374 
GetTestCases(void)375 void AttributeLayoutTest::GetTestCases(void)
376 {
377     const bool es3 = getClientMajorVersion() >= 3;
378 
379     Format<GLfloat, GL_FLOAT, false> Float(es3);
380     Format<GLint, GL_FIXED, false> Fixed(es3);
381 
382     Format<GLbyte, GL_BYTE, false> SByte(es3);
383     Format<GLubyte, GL_UNSIGNED_BYTE, false> UByte(es3);
384     Format<GLshort, GL_SHORT, false> SShort(es3);
385     Format<GLushort, GL_UNSIGNED_SHORT, false> UShort(es3);
386     Format<GLint, GL_INT, false> SInt(es3);
387     Format<GLuint, GL_UNSIGNED_INT, false> UInt(es3);
388 
389     Format<GLbyte, GL_BYTE, true> NormSByte(es3);
390     Format<GLubyte, GL_UNSIGNED_BYTE, true> NormUByte(es3);
391     Format<GLshort, GL_SHORT, true> NormSShort(es3);
392     Format<GLushort, GL_UNSIGNED_SHORT, true> NormUShort(es3);
393     Format<GLint, GL_INT, true> NormSInt(es3);
394     Format<GLuint, GL_UNSIGNED_INT, true> NormUInt(es3);
395 
396     std::shared_ptr<Container> M0 = std::make_shared<Memory>();
397     std::shared_ptr<Container> M1 = std::make_shared<Memory>();
398     std::shared_ptr<Container> B0 = std::make_shared<Buffer>();
399     std::shared_ptr<Container> B1 = std::make_shared<Buffer>();
400 
401     // 0. two buffers
402     mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B1, 0, 12, mColor)});
403 
404     // 1. two memory
405     mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M1, 0, 12, mColor)});
406 
407     // 2. one memory, sequential
408     mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M0, 96, 12, mColor)});
409 
410     // 3. one memory, interleaved
411     mTestCases.push_back({Float(M0, 0, 20, mCoord), Float(M0, 8, 20, mColor)});
412 
413     // 4. buffer and memory
414     mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(M0, 0, 12, mColor)});
415 
416     // 5. stride != size
417     mTestCases.push_back({Float(B0, 0, 16, mCoord), Float(B1, 0, 12, mColor)});
418 
419     // 6-7. same stride and format, switching data between memory and buffer
420     mTestCases.push_back({Float(M0, 0, 16, mCoord), Float(M1, 0, 12, mColor)});
421     mTestCases.push_back({Float(B0, 0, 16, mCoord), Float(B1, 0, 12, mColor)});
422 
423     // 8-9. same stride and format, offset change
424     mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B1, 0, 12, mColor)});
425     mTestCases.push_back({Float(B0, 3, 8, mCoord), Float(B1, 4, 12, mColor)});
426 
427     // 10-11. unaligned buffer data
428     mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(B0, 1, 13, mColor)});
429     mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(B1, 1, 13, mColor)});
430 
431     // 12-15. byte/short
432     mTestCases.push_back({SByte(M0, 0, 20, mCoord), UByte(M0, 10, 20, mColor)});
433     mTestCases.push_back({SShort(M0, 0, 20, mCoord), UShort(M0, 8, 20, mColor)});
434     mTestCases.push_back({NormSByte(M0, 0, 8, mCoord), NormUByte(M0, 4, 8, mColor)});
435     mTestCases.push_back({NormSShort(M0, 0, 20, mCoord), NormUShort(M0, 8, 20, mColor)});
436 
437     // 16. one buffer, sequential
438     mTestCases.push_back({Fixed(B0, 0, 8, mCoord), Float(B0, 96, 12, mColor)});
439 
440     // 17. one buffer, interleaved
441     mTestCases.push_back({Fixed(B0, 0, 20, mCoord), Float(B0, 8, 20, mColor)});
442 
443     // 18. memory and buffer, float and integer
444     mTestCases.push_back({Float(M0, 0, 8, mCoord), SByte(B0, 0, 12, mColor)});
445 
446     // 19. buffer and memory, unusual offset and stride
447     mTestCases.push_back({Float(B0, 11, 13, mCoord), Float(M0, 23, 17, mColor)});
448 
449     // 20-21. remaining ES3 formats
450     if (es3)
451     {
452         mTestCases.push_back({SInt(M0, 0, 40, mCoord), UInt(M0, 16, 40, mColor)});
453         // Fails on Nexus devices (anglebug.com/42261348)
454         if (!IsNexus5X())
455             mTestCases.push_back({NormSInt(M0, 0, 40, mCoord), NormUInt(M0, 16, 40, mColor)});
456     }
457 }
458 
459 class AttributeLayoutNonIndexed : public AttributeLayoutTest
460 {
Draw(int firstVertex,unsigned vertexCount,const GLushort * indices)461     void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override
462     {
463         glDrawArrays(GL_TRIANGLES, firstVertex, vertexCount);
464     }
465 };
466 
467 class AttributeLayoutMemoryIndexed : public AttributeLayoutTest
468 {
Draw(int firstVertex,unsigned vertexCount,const GLushort * indices)469     void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override
470     {
471         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
472         glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, indices);
473     }
474 };
475 
476 class AttributeLayoutBufferIndexed : public AttributeLayoutTest
477 {
Draw(int firstVertex,unsigned vertexCount,const GLushort * indices)478     void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override
479     {
480         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
481         glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(*mIndices) * vertexCount, indices,
482                      GL_STATIC_DRAW);
483         glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, nullptr);
484     }
485 };
486 
TEST_P(AttributeLayoutNonIndexed,Test)487 TEST_P(AttributeLayoutNonIndexed, Test)
488 {
489     Run(true);
490     ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
491     Run(false);
492 }
493 
TEST_P(AttributeLayoutMemoryIndexed,Test)494 TEST_P(AttributeLayoutMemoryIndexed, Test)
495 {
496     Run(true);
497     ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
498     Run(false);
499 }
500 
TEST_P(AttributeLayoutBufferIndexed,Test)501 TEST_P(AttributeLayoutBufferIndexed, Test)
502 {
503     Run(true);
504     ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
505     Run(false);
506 }
507 
508 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(AttributeLayoutNonIndexed,
509                                        ES3_VULKAN()
510                                            .disable(Feature::SupportsExtendedDynamicState)
511                                            .disable(Feature::SupportsExtendedDynamicState2));
512 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(AttributeLayoutMemoryIndexed,
513                                        ES3_VULKAN()
514                                            .disable(Feature::SupportsExtendedDynamicState)
515                                            .disable(Feature::SupportsExtendedDynamicState2));
516 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(AttributeLayoutBufferIndexed,
517                                        ES3_VULKAN()
518                                            .disable(Feature::SupportsExtendedDynamicState)
519                                            .disable(Feature::SupportsExtendedDynamicState2));
520 
521 #define STRINGIFY2(X) #X
522 #define STRINGIFY(X) STRINGIFY2(X)
523 
524 // clang-format off
525 #define VS_SHADER(ColorDataType) \
526 "#version 300 es\n"\
527 "in highp vec2 coord;\n"\
528 "in highp " STRINGIFY(ColorDataType) " color;\n"\
529 "flat out highp " STRINGIFY(ColorDataType) " vcolor;\n"\
530 "void main(void)\n"\
531 "{\n"\
532 "    gl_Position = vec4(coord, 0, 1);\n"\
533 "    vcolor = color;\n"\
534 "}\n"
535 
536 #define PS_SHADER(ColorDataType) \
537 "#version 300 es\n"\
538 "flat in highp " STRINGIFY(ColorDataType) " vcolor;\n"\
539 "out highp " STRINGIFY(ColorDataType) " outColor;\n"\
540 "void main(void)\n"\
541 "{\n"\
542 "    outColor = vcolor;\n"\
543 "}\n"
544 
545 // clang-format on
546 
547 // clang-format off
548 constexpr double kVertexData[] = {
549    //x   y       rgba
550     -1, -1,      128, 128, 93, 255,
551     +1, -1,      128, 128, 93, 255,
552     -1, +1,      128, 128, 93, 255,
553     +1, +1,      128, 128, 93, 255,
554 };
555 // clang-format on
556 
557 template <class ResType>
GetRefValue(const void * data,GLenum glType)558 ResType GetRefValue(const void *data, GLenum glType)
559 {
560     switch (glType)
561     {
562         case GL_BYTE:
563         {
564             const int8_t *p = reinterpret_cast<const int8_t *>(data);
565             return ResType(p[0], p[1], p[2], p[3]);
566         }
567         case GL_SHORT:
568         case GL_HALF_FLOAT:
569         {
570             const int16_t *p = reinterpret_cast<const int16_t *>(data);
571             return ResType(p[0], p[1], p[2], p[3]);
572         }
573         case GL_INT:
574         case GL_FIXED:
575         {
576             const int32_t *p = reinterpret_cast<const int32_t *>(data);
577             return ResType(p[0], p[1], p[2], p[3]);
578         }
579         case GL_UNSIGNED_BYTE:
580         {
581             const uint8_t *p = reinterpret_cast<const uint8_t *>(data);
582             return ResType(p[0], p[1], p[2], p[3]);
583         }
584         case GL_UNSIGNED_SHORT:
585         {
586             const uint16_t *p = reinterpret_cast<const uint16_t *>(data);
587             return ResType(p[0], p[1], p[2], p[3]);
588         }
589         case GL_FLOAT:
590         case GL_UNSIGNED_INT:
591         {
592             const uint32_t *p = reinterpret_cast<const uint32_t *>(data);
593             return ResType(p[0], p[1], p[2], p[3]);
594         }
595         default:
596         {
597             ASSERT(0);
598             const uint32_t *p = reinterpret_cast<const uint32_t *>(data);
599             return ResType(p[0], p[1], p[2], p[3]);
600         }
601     }
602 }
603 
604 constexpr size_t kIndexCount = 6;
605 constexpr int kRboSize       = 8;
606 
ConvertFloatToUnorm8(const GLColor32F & color32f)607 GLColor ConvertFloatToUnorm8(const GLColor32F &color32f)
608 {
609     float r = std::clamp(color32f.R, 0.0f, 1.0f);
610     float g = std::clamp(color32f.G, 0.0f, 1.0f);
611     float b = std::clamp(color32f.B, 0.0f, 1.0f);
612     float a = std::clamp(color32f.A, 0.0f, 1.0f);
613     return GLColor(std::round(r * 255), std::round(g * 255), std::round(b * 255),
614                    std::round(a * 255));
615 }
616 
BindAttribLocation(GLuint program)617 void BindAttribLocation(GLuint program)
618 {
619     glBindAttribLocation(program, 0, "coord");
620     glBindAttribLocation(program, 1, "color");
621 }
622 
623 class AttributeDataTypeMismatchTest : public ANGLETest<>
624 {
625   public:
626     enum VsInputDataType
627     {
628         FLOAT    = 0,
629         INT      = 1,
630         UNSIGNED = 2,
631         COUNT    = 3,
632     };
633 
634   protected:
AttributeDataTypeMismatchTest()635     AttributeDataTypeMismatchTest()
636         : mCoord(2, kVertexData, 0, 6, 4), mColor(4, kVertexData, 2, 6, 4)
637     {
638         setWindowWidth(128);
639         setWindowHeight(128);
640         setConfigRedBits(8);
641         setConfigGreenBits(8);
642         setConfigBlueBits(8);
643         setConfigAlphaBits(8);
644     }
645 
createFbo(GLuint rbo)646     GLuint createFbo(GLuint rbo)
647     {
648 
649         GLuint fbo = 0;
650         glGenFramebuffers(1, &fbo);
651         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
652         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
653         glBindFramebuffer(GL_FRAMEBUFFER, 0);
654         return fbo;
655     }
656 
createRbo(VsInputDataType inputDataType)657     GLuint createRbo(VsInputDataType inputDataType)
658     {
659         GLuint rbo = 0;
660         glGenRenderbuffers(1, &rbo);
661         GLenum format = GL_RGBA8;
662         if (inputDataType == VsInputDataType::INT)
663         {
664             format = GL_RGBA32I;
665         }
666         else if (inputDataType == VsInputDataType::UNSIGNED)
667         {
668             format = GL_RGBA32UI;
669         }
670         glBindRenderbuffer(GL_RENDERBUFFER, rbo);
671         glRenderbufferStorage(GL_RENDERBUFFER, format, kRboSize, kRboSize);
672         glBindRenderbuffer(GL_RENDERBUFFER, 0);
673         return rbo;
674     }
675 
testSetUp()676     void testSetUp() override
677     {
678         glClearColor(.2f, .2f, .2f, .0f);
679         glClear(GL_COLOR_BUFFER_BIT);
680 
681         glDisable(GL_DEPTH_TEST);
682 
683         constexpr const char *kVS[VsInputDataType::COUNT] = {
684             VS_SHADER(vec4),
685             VS_SHADER(ivec4),
686             VS_SHADER(uvec4),
687         };
688 
689         constexpr const char *kFS[VsInputDataType::COUNT] = {
690             PS_SHADER(vec4),
691             PS_SHADER(ivec4),
692             PS_SHADER(uvec4),
693         };
694 
695         for (int i = VsInputDataType::FLOAT; i < VsInputDataType::COUNT; ++i)
696         {
697             mProgram[i] = CompileProgram(kVS[i], kFS[i], BindAttribLocation);
698             ASSERT_NE(0u, mProgram[i]);
699             mRbo[i] = createRbo(static_cast<VsInputDataType>(i));
700             ASSERT_NE(0u, mRbo[i]);
701             mFbo[i] = createFbo(mRbo[i]);
702             ASSERT_NE(0u, mFbo[i]);
703         }
704 
705         glGenBuffers(1, &mIndexBuffer);
706         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
707         glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(mIndices), mIndices, GL_STATIC_DRAW);
708         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
709     }
710 
GetTestCases(VsInputDataType dataType)711     void GetTestCases(VsInputDataType dataType)
712     {
713         const bool es3 = getClientMajorVersion() >= 3;
714 
715         std::shared_ptr<Container> B0 = std::make_shared<Buffer>();
716         if (dataType != VsInputDataType::FLOAT)
717         {
718             // float and fixed.
719             Format<GLfloat, GL_FLOAT, false> Float(es3);
720             Format<GLint, GL_FIXED, false> Fixed(es3);
721             Format<GLshort, GL_HALF_FLOAT, false> halfFloat(es3);
722             // for UScale, SScale.
723             Format<GLbyte, GL_BYTE, false> SByte(es3);
724             Format<GLubyte, GL_UNSIGNED_BYTE, false> UByte(es3);
725             Format<GLshort, GL_SHORT, false> SShort(es3);
726             Format<GLushort, GL_UNSIGNED_SHORT, false> UShort(es3);
727             // UScale32, Scale32 may emulated. testing unsigned<-->int
728             Format<GLint, GL_INT, false, true> SInt(es3);
729             Format<GLuint, GL_UNSIGNED_INT, false, true> UInt(es3);
730             mTestCases.push_back({Float(B0, 0, 12, mCoord), SByte(B0, 8, 12, mColor)});
731             mTestCases.push_back({Float(B0, 0, 12, mCoord), UByte(B0, 8, 12, mColor)});
732             mTestCases.push_back({Float(B0, 0, 16, mCoord), SShort(B0, 8, 16, mColor)});
733             mTestCases.push_back({Float(B0, 0, 16, mCoord), UShort(B0, 8, 16, mColor)});
734             mTestCases.push_back({Float(B0, 0, 16, mCoord), halfFloat(B0, 8, 16, mColor)});
735             mTestCases.push_back({Float(B0, 0, 24, mCoord), SInt(B0, 8, 24, mColor)});
736             mTestCases.push_back({Float(B0, 0, 24, mCoord), UInt(B0, 8, 24, mColor)});
737             mTestCases.push_back({Float(B0, 0, 24, mCoord), Float(B0, 8, 24, mColor)});
738             // for GL_FIXED, angle may use GLfloat emulated.
739             // mTestCases.push_back({Float(B0, 0, 24, mCoord), Fixed(B0, 8, 24, mColor)});
740         }
741         else
742         {
743             Format<GLfloat, GL_FLOAT, false> Float(es3);
744             Format<GLbyte, GL_BYTE, false, true> SByte(es3);
745             Format<GLubyte, GL_UNSIGNED_BYTE, false, true> UByte(es3);
746             Format<GLshort, GL_SHORT, false, true> SShort(es3);
747             Format<GLushort, GL_UNSIGNED_SHORT, false, true> UShort(es3);
748             Format<GLint, GL_INT, false, true> SInt(es3);
749             Format<GLuint, GL_UNSIGNED_INT, false, true> UInt(es3);
750             mTestCases.push_back({Float(B0, 0, 12, mCoord), SByte(B0, 8, 12, mColor)});
751             mTestCases.push_back({Float(B0, 0, 12, mCoord), UByte(B0, 8, 12, mColor)});
752             mTestCases.push_back({Float(B0, 0, 16, mCoord), SShort(B0, 8, 16, mColor)});
753             mTestCases.push_back({Float(B0, 0, 16, mCoord), UShort(B0, 8, 16, mColor)});
754             // UScale32, Scale32 may emulated.
755             // mTestCases.push_back({Float(B0, 0, 24, mCoord), SInt(B0, 8, 24, mColor)});
756             // mTestCases.push_back({Float(B0, 0, 24, mCoord), UInt(B0, 8, 24, mColor)});
757         }
758     }
759 
testTearDown()760     void testTearDown() override
761     {
762         mTestCases.clear();
763         for (int i = 0; i < VsInputDataType::COUNT; ++i)
764         {
765             glDeleteProgram(mProgram[i]);
766             glDeleteFramebuffers(1, &mFbo[i]);
767             glDeleteRenderbuffers(1, &mRbo[i]);
768         }
769         glDeleteBuffers(1, &mIndexBuffer);
770     }
771 
GetMappedGLType(GLenum glType,VsInputDataType vsInputDataType)772     GLenum GetMappedGLType(GLenum glType, VsInputDataType vsInputDataType)
773     {
774         switch (glType)
775         {
776             case GL_BYTE:
777                 return vsInputDataType != VsInputDataType::UNSIGNED ? GL_BYTE : GL_UNSIGNED_BYTE;
778             case GL_SHORT:
779             case GL_HALF_FLOAT:
780                 return vsInputDataType != VsInputDataType::UNSIGNED ? GL_SHORT : GL_UNSIGNED_SHORT;
781             case GL_INT:
782             case GL_FIXED:
783                 return vsInputDataType != VsInputDataType::UNSIGNED ? GL_INT : GL_UNSIGNED_INT;
784             case GL_UNSIGNED_BYTE:
785                 return vsInputDataType != VsInputDataType::INT ? GL_UNSIGNED_BYTE : GL_BYTE;
786             case GL_UNSIGNED_SHORT:
787                 return vsInputDataType != VsInputDataType::INT ? GL_UNSIGNED_SHORT : GL_SHORT;
788             case GL_FLOAT:
789             case GL_UNSIGNED_INT:
790                 return vsInputDataType != VsInputDataType::INT ? GL_UNSIGNED_INT : GL_INT;
791             default:
792                 ASSERT(0);
793                 return vsInputDataType != VsInputDataType::INT ? GL_UNSIGNED_INT : GL_INT;
794         }
795     }
796 
Run(VsInputDataType dataType)797     void Run(VsInputDataType dataType)
798     {
799         GetTestCases(dataType);
800         ASSERT(dataType < VsInputDataType::COUNT);
801         glBindFramebuffer(GL_FRAMEBUFFER, mFbo[dataType]);
802         glViewport(0, 0, kRboSize, kRboSize);
803         glUseProgram(mProgram[dataType]);
804         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
805         for (unsigned i = 0; i < mTestCases.size(); ++i)
806         {
807             if (mTestCases[i].size() == 0)
808                 continue;
809             ASSERT(mTestCases[i].size() == 2);
810             PrepareTestCase(mTestCases[i]);
811             EXPECT_GL_NO_ERROR();
812             GLint iClearValue[]   = {0, 0, 0, 1};
813             GLfloat fClearValue[] = {1.0f, 0.0f, 0.0f, 1.0f};
814             switch (dataType)
815             {
816                 case VsInputDataType::FLOAT:
817                     glClearBufferfv(GL_COLOR, 0, fClearValue);
818                     break;
819                 case VsInputDataType::INT:
820                     glClearBufferiv(GL_COLOR, 0, iClearValue);
821                     break;
822                 case VsInputDataType::UNSIGNED:
823                     glClearBufferuiv(GL_COLOR, 0, reinterpret_cast<const GLuint *>(iClearValue));
824                     break;
825                 default:
826                     ASSERT(0);
827             }
828             EXPECT_GL_NO_ERROR();
829             glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
830             EXPECT_GL_NO_ERROR();
831 
832             std::shared_ptr<Container> container = mTestCases[i][1].mContainer;
833             size_t offset                        = mTestCases[i][1].mOffset;
834             GLenum glType = GetMappedGLType(mTestCases[i][1].mGLType, dataType);
835             switch (dataType)
836             {
837                 case VsInputDataType::FLOAT:
838                     EXPECT_PIXEL_COLOR_EQ(0, 0,
839                                           ConvertFloatToUnorm8(GetRefValue<GLColor32F>(
840                                               container->getDestination(offset), glType)));
841                     break;
842                 case VsInputDataType::INT:
843                     EXPECT_PIXEL_RECT32I_EQ(
844                         0, 0, 1, 1,
845                         GetRefValue<GLColor32I>(container->getDestination(offset), glType));
846                     break;
847                 case VsInputDataType::UNSIGNED:
848                     EXPECT_PIXEL_RECT32UI_EQ(
849                         0, 0, 1, 1,
850                         GetRefValue<GLColor32UI>(container->getDestination(offset), glType));
851                     break;
852                 default:
853                     ASSERT(0);
854             }
855         }
856         mTestCases.clear();
857     }
858 
859     static const GLushort mIndices[kIndexCount];
860 
861     GLuint mProgram[VsInputDataType::COUNT];
862     GLuint mFbo[VsInputDataType::COUNT];
863     GLuint mRbo[VsInputDataType::COUNT];
864     GLuint mIndexBuffer;
865 
866     std::vector<TestCase> mTestCases;
867 
868     VertexData mCoord;
869     VertexData mColor;
870 };
871 
872 const GLushort AttributeDataTypeMismatchTest::mIndices[kIndexCount] = {0, 1, 2, 2, 1, 3};
873 
874 // Test Attribute input data type mismatch with vertex shader input.
875 // Change the attribute input data type to vertex shader input data type.
TEST_P(AttributeDataTypeMismatchTest,Test)876 TEST_P(AttributeDataTypeMismatchTest, Test)
877 {
878     // At some device. UScale and Scale are emulated.
879     // Restrict tests running at nvidia device only.
880     ANGLE_SKIP_TEST_IF(!IsVulkan() || !IsNVIDIA());
881     Run(VsInputDataType::FLOAT);
882     Run(VsInputDataType::INT);
883     Run(VsInputDataType::UNSIGNED);
884 }
885 
886 ANGLE_INSTANTIATE_TEST_ES3(AttributeDataTypeMismatchTest);
887 
888 }  // anonymous namespace
889