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