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 
7 // MultiDrawTest: Tests of GL_ANGLE_multi_draw
8 
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11 
12 using namespace angle;
13 
14 namespace
15 {
16 
17 // Create a kWidth * kHeight canvas equally split into kCountX * kCountY tiles
18 // each containing a quad partially coverting each tile
19 constexpr uint32_t kWidth                  = 256;
20 constexpr uint32_t kHeight                 = 256;
21 constexpr uint32_t kCountX                 = 8;
22 constexpr uint32_t kCountY                 = 8;
23 constexpr uint32_t kQuadCount              = kCountX * kCountY;
24 constexpr uint32_t kTriCount               = kQuadCount * 2;
25 constexpr std::array<GLfloat, 2> kTileSize = {
26     1.f / static_cast<GLfloat>(kCountX),
27     1.f / static_cast<GLfloat>(kCountY),
28 };
29 constexpr std::array<uint32_t, 2> kTilePixelSize  = {kWidth / kCountX, kHeight / kCountY};
30 constexpr std::array<GLfloat, 2> kQuadRadius      = {0.25f * kTileSize[0], 0.25f * kTileSize[1]};
31 constexpr std::array<uint32_t, 2> kPixelCheckSize = {
32     static_cast<uint32_t>(kQuadRadius[0] * kWidth),
33     static_cast<uint32_t>(kQuadRadius[1] * kHeight)};
34 
getTileCenter(uint32_t x,uint32_t y)35 constexpr std::array<GLfloat, 2> getTileCenter(uint32_t x, uint32_t y)
36 {
37     return {
38         kTileSize[0] * (0.5f + static_cast<GLfloat>(x)),
39         kTileSize[1] * (0.5f + static_cast<GLfloat>(y)),
40     };
41 }
getQuadVertices(uint32_t x,uint32_t y)42 constexpr std::array<std::array<GLfloat, 3>, 4> getQuadVertices(uint32_t x, uint32_t y)
43 {
44     const auto center = getTileCenter(x, y);
45     return {
46         std::array<GLfloat, 3>{center[0] - kQuadRadius[0], center[1] - kQuadRadius[1], 0.0f},
47         std::array<GLfloat, 3>{center[0] + kQuadRadius[0], center[1] - kQuadRadius[1], 0.0f},
48         std::array<GLfloat, 3>{center[0] + kQuadRadius[0], center[1] + kQuadRadius[1], 0.0f},
49         std::array<GLfloat, 3>{center[0] - kQuadRadius[0], center[1] + kQuadRadius[1], 0.0f},
50     };
51 }
52 
53 enum class DrawIDOption
54 {
55     NoDrawID,
56     UseDrawID,
57 };
58 
59 enum class InstancingOption
60 {
61     NoInstancing,
62     UseInstancing,
63 };
64 
65 enum class BufferDataUsageOption
66 {
67     StaticDraw,
68     DynamicDraw
69 };
70 
71 using MultiDrawTestParams =
72     std::tuple<angle::PlatformParameters, DrawIDOption, InstancingOption, BufferDataUsageOption>;
73 
74 struct PrintToStringParamName
75 {
operator ()__anon41bb8f9e0111::PrintToStringParamName76     std::string operator()(const ::testing::TestParamInfo<MultiDrawTestParams> &info) const
77     {
78         ::std::stringstream ss;
79         ss << std::get<0>(info.param)
80            << (std::get<3>(info.param) == BufferDataUsageOption::StaticDraw ? "__StaticDraw"
81                                                                             : "__DynamicDraw")
82            << (std::get<2>(info.param) == InstancingOption::UseInstancing ? "__Instanced" : "")
83            << (std::get<1>(info.param) == DrawIDOption::UseDrawID ? "__DrawID" : "");
84         return ss.str();
85     }
86 };
87 
88 // These tests check correctness of the ANGLE_multi_draw extension.
89 // An array of quads is drawn across the screen.
90 // gl_DrawID is checked by using it to select the color of the draw.
91 // MultiDraw*Instanced entrypoints use the existing instancing APIs which are
92 // more fully tested in InstancingTest.cpp.
93 // Correct interaction with the instancing APIs is tested here by using scaling
94 // and then instancing the array of quads over four quadrants on the screen.
95 class MultiDrawTest : public ANGLETestBase, public ::testing::TestWithParam<MultiDrawTestParams>
96 {
97   protected:
MultiDrawTest()98     MultiDrawTest()
99         : ANGLETestBase(std::get<0>(GetParam())),
100           mNonIndexedVertexBuffer(0u),
101           mVertexBuffer(0u),
102           mIndexBuffer(0u),
103           mInstanceBuffer(0u),
104           mProgram(0u)
105     {
106         setWindowWidth(kWidth);
107         setWindowHeight(kHeight);
108         setConfigRedBits(8);
109         setConfigGreenBits(8);
110         setConfigBlueBits(8);
111         setConfigAlphaBits(8);
112     }
113 
SetUp()114     void SetUp() override { ANGLETestBase::ANGLETestSetUp(); }
115 
IsDrawIDTest() const116     bool IsDrawIDTest() const { return std::get<1>(GetParam()) == DrawIDOption::UseDrawID; }
117 
IsInstancedTest() const118     bool IsInstancedTest() const
119     {
120         return std::get<2>(GetParam()) == InstancingOption::UseInstancing;
121     }
122 
getBufferDataUsage() const123     GLenum getBufferDataUsage() const
124     {
125         return std::get<3>(GetParam()) == BufferDataUsageOption::StaticDraw ? GL_STATIC_DRAW
126                                                                             : GL_DYNAMIC_DRAW;
127     }
128 
VertexShaderSource()129     std::string VertexShaderSource()
130     {
131 
132         std::stringstream shader;
133         shader << (IsDrawIDTest() ? "#extension GL_ANGLE_multi_draw : require\n" : "")
134                << (IsInstancedTest() ? "attribute float vInstance;" : "") << R"(
135 attribute vec2 vPosition;
136 varying vec4 color;
137 void main()
138 {
139     int id = )" << (IsDrawIDTest() ? "gl_DrawID" : "0")
140                << ";"
141                << R"(
142     float quad_id = float(id / 2);
143     float color_id = quad_id - (3.0 * floor(quad_id / 3.0));
144     if (color_id == 0.0) {
145       color = vec4(1, 0, 0, 1);
146     } else if (color_id == 1.0) {
147       color = vec4(0, 1, 0, 1);
148     } else {
149       color = vec4(0, 0, 1, 1);
150     }
151 
152     mat3 transform = mat3(1.0);
153 )"
154                << (IsInstancedTest() ? R"(
155     transform[0][0] = 0.5;
156     transform[1][1] = 0.5;
157     if (vInstance == 0.0) {
158 
159     } else if (vInstance == 1.0) {
160         transform[2][0] = 0.5;
161     } else if (vInstance == 2.0) {
162         transform[2][1] = 0.5;
163     } else if (vInstance == 3.0) {
164         transform[2][0] = 0.5;
165         transform[2][1] = 0.5;
166     }
167 )"
168                                      : "")
169                << R"(
170     gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
171 })";
172 
173         return shader.str();
174     }
175 
FragmentShaderSource()176     std::string FragmentShaderSource()
177     {
178         return
179             R"(precision mediump float;
180             varying vec4 color;
181             void main()
182             {
183                 gl_FragColor = color;
184             })";
185     }
186 
SetupProgram()187     void SetupProgram()
188     {
189         mProgram = CompileProgram(VertexShaderSource().c_str(), FragmentShaderSource().c_str());
190         EXPECT_GL_NO_ERROR();
191         ASSERT_GE(mProgram, 1u);
192         glUseProgram(mProgram);
193         mPositionLoc = glGetAttribLocation(mProgram, "vPosition");
194         mInstanceLoc = glGetAttribLocation(mProgram, "vInstance");
195     }
196 
SetupBuffers()197     void SetupBuffers()
198     {
199         for (uint32_t y = 0; y < kCountY; ++y)
200         {
201             for (uint32_t x = 0; x < kCountX; ++x)
202             {
203                 // v3 ---- v2
204                 // |       |
205                 // |       |
206                 // v0 ---- v1
207                 uint32_t quadIndex         = y * kCountX + x;
208                 GLushort starting_index    = static_cast<GLushort>(4 * quadIndex);
209                 std::array<GLushort, 6> is = {0, 1, 2, 0, 2, 3};
210                 const auto vs              = getQuadVertices(x, y);
211                 for (GLushort i : is)
212                 {
213                     mIndices.push_back(starting_index + i);
214                 }
215 
216                 for (const auto &v : vs)
217                 {
218                     mVertices.insert(mVertices.end(), v.begin(), v.end());
219                 }
220 
221                 for (GLushort i : is)
222                 {
223                     mNonIndexedVertices.insert(mNonIndexedVertices.end(), vs[i].begin(),
224                                                vs[i].end());
225                 }
226             }
227         }
228 
229         std::array<GLfloat, 4> instances{0, 1, 2, 3};
230 
231         glGenBuffers(1, &mNonIndexedVertexBuffer);
232         glBindBuffer(GL_ARRAY_BUFFER, mNonIndexedVertexBuffer);
233         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mNonIndexedVertices.size(),
234                      mNonIndexedVertices.data(), getBufferDataUsage());
235 
236         glGenBuffers(1, &mVertexBuffer);
237         glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
238         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mVertices.size(), mVertices.data(),
239                      getBufferDataUsage());
240 
241         glGenBuffers(1, &mIndexBuffer);
242         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
243         glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * mIndices.size(), mIndices.data(),
244                      getBufferDataUsage());
245 
246         glGenBuffers(1, &mInstanceBuffer);
247         glBindBuffer(GL_ARRAY_BUFFER, mInstanceBuffer);
248         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * instances.size(), instances.data(),
249                      getBufferDataUsage());
250 
251         ASSERT_GL_NO_ERROR();
252     }
253 
DoVertexAttribDivisor(GLint location,GLuint divisor)254     void DoVertexAttribDivisor(GLint location, GLuint divisor)
255     {
256         if (getClientMajorVersion() <= 2)
257         {
258             ASSERT_TRUE(IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
259             glVertexAttribDivisorANGLE(location, divisor);
260         }
261         else
262         {
263             glVertexAttribDivisor(location, divisor);
264         }
265     }
266 
DoDrawArrays()267     void DoDrawArrays()
268     {
269         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
270         glBindBuffer(GL_ARRAY_BUFFER, mNonIndexedVertexBuffer);
271         glEnableVertexAttribArray(mPositionLoc);
272         glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
273 
274         std::vector<GLint> firsts(kTriCount);
275         std::vector<GLsizei> counts(kTriCount, 3);
276         for (uint32_t i = 0; i < kTriCount; ++i)
277             firsts[i] = i * 3;
278 
279         if (IsInstancedTest())
280         {
281             glBindBuffer(GL_ARRAY_BUFFER, mInstanceBuffer);
282             glEnableVertexAttribArray(mInstanceLoc);
283             glVertexAttribPointer(mInstanceLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
284             DoVertexAttribDivisor(mInstanceLoc, 1);
285             std::vector<GLsizei> instanceCounts(kTriCount, 4);
286             glMultiDrawArraysInstancedANGLE(GL_TRIANGLES, firsts.data(), counts.data(),
287                                             instanceCounts.data(), kTriCount);
288         }
289         else
290         {
291             glMultiDrawArraysANGLE(GL_TRIANGLES, firsts.data(), counts.data(), kTriCount);
292         }
293     }
294 
DoDrawElements()295     void DoDrawElements()
296     {
297         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
298         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
299         glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
300         glEnableVertexAttribArray(mPositionLoc);
301         glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
302 
303         std::vector<GLsizei> counts(kTriCount, 3);
304         std::vector<const GLvoid *> indices(kTriCount);
305         for (uint32_t i = 0; i < kTriCount; ++i)
306             indices[i] = reinterpret_cast<GLvoid *>(static_cast<uintptr_t>(i * 3 * 2));
307 
308         if (IsInstancedTest())
309         {
310             glBindBuffer(GL_ARRAY_BUFFER, mInstanceBuffer);
311             glEnableVertexAttribArray(mInstanceLoc);
312             glVertexAttribPointer(mInstanceLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
313             DoVertexAttribDivisor(mInstanceLoc, 1);
314             std::vector<GLsizei> instanceCounts(kTriCount, 4);
315             glMultiDrawElementsInstancedANGLE(GL_TRIANGLES, counts.data(), GL_UNSIGNED_SHORT,
316                                               indices.data(), instanceCounts.data(), kTriCount);
317         }
318         else
319         {
320             glMultiDrawElementsANGLE(GL_TRIANGLES, counts.data(), GL_UNSIGNED_SHORT, indices.data(),
321                                      kTriCount);
322         }
323     }
324 
CheckDrawResult()325     void CheckDrawResult()
326     {
327         for (uint32_t y = 0; y < kCountY; ++y)
328         {
329             for (uint32_t x = 0; x < kCountX; ++x)
330             {
331                 uint32_t center_x             = x * kTilePixelSize[0] + kTilePixelSize[0] / 2;
332                 uint32_t center_y             = y * kTilePixelSize[1] + kTilePixelSize[1] / 2;
333                 uint32_t quadID               = IsDrawIDTest() ? y * kCountX + x : 0;
334                 uint32_t colorID              = quadID % 3u;
335                 std::array<GLColor, 3> colors = {GLColor(255, 0, 0, 255), GLColor(0, 255, 0, 255),
336                                                  GLColor(0, 0, 255, 255)};
337                 GLColor expected              = colors[colorID];
338 
339                 if (IsInstancedTest())
340                 {
341                     EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4,
342                                          center_y / 2 - kPixelCheckSize[1] / 4,
343                                          kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
344                     EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4 + kWidth / 2,
345                                          center_y / 2 - kPixelCheckSize[1] / 4,
346                                          kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
347                     EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4,
348                                          center_y / 2 - kPixelCheckSize[1] / 4 + kHeight / 2,
349                                          kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
350                     EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4 + kWidth / 2,
351                                          center_y / 2 - kPixelCheckSize[1] / 4 + kHeight / 2,
352                                          kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
353                 }
354                 else
355                 {
356                     EXPECT_PIXEL_RECT_EQ(center_x - kPixelCheckSize[0] / 2,
357                                          center_y - kPixelCheckSize[1] / 2, kPixelCheckSize[0],
358                                          kPixelCheckSize[1], expected);
359                 }
360             }
361         }
362     }
363 
TearDown()364     void TearDown() override
365     {
366         if (mNonIndexedVertexBuffer != 0u)
367         {
368             glDeleteBuffers(1, &mNonIndexedVertexBuffer);
369         }
370         if (mVertexBuffer != 0u)
371         {
372             glDeleteBuffers(1, &mVertexBuffer);
373         }
374         if (mIndexBuffer != 0u)
375         {
376             glDeleteBuffers(1, &mIndexBuffer);
377         }
378         if (mInstanceBuffer != 0u)
379         {
380             glDeleteBuffers(1, &mInstanceBuffer);
381         }
382         if (mProgram != 0)
383         {
384             glDeleteProgram(mProgram);
385         }
386         ANGLETestBase::ANGLETestTearDown();
387     }
388 
requestMultiDrawExtension()389     bool requestMultiDrawExtension()
390     {
391         if (IsGLExtensionRequestable("GL_ANGLE_multi_draw"))
392         {
393             glRequestExtensionANGLE("GL_ANGLE_multi_draw");
394         }
395 
396         if (!IsGLExtensionEnabled("GL_ANGLE_multi_draw"))
397         {
398             return false;
399         }
400 
401         return true;
402     }
403 
requestInstancedExtension()404     bool requestInstancedExtension()
405     {
406         if (IsGLExtensionRequestable("GL_ANGLE_instanced_arrays"))
407         {
408             glRequestExtensionANGLE("GL_ANGLE_instanced_arrays");
409         }
410 
411         if (!IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"))
412         {
413             return false;
414         }
415 
416         return true;
417     }
418 
requestExtensions()419     bool requestExtensions()
420     {
421         if (IsInstancedTest() && getClientMajorVersion() <= 2)
422         {
423             if (!requestInstancedExtension())
424             {
425                 return false;
426             }
427         }
428         return requestMultiDrawExtension();
429     }
430 
431     std::vector<GLushort> mIndices;
432     std::vector<GLfloat> mVertices;
433     std::vector<GLfloat> mNonIndexedVertices;
434     GLuint mNonIndexedVertexBuffer;
435     GLuint mVertexBuffer;
436     GLuint mIndexBuffer;
437     GLuint mInstanceBuffer;
438     GLuint mProgram;
439     GLint mPositionLoc;
440     GLint mInstanceLoc;
441 };
442 
443 class MultiDrawNoInstancingSupportTest : public MultiDrawTest
444 {
SetUp()445     void SetUp() override
446     {
447         ASSERT_LE(getClientMajorVersion(), 2);
448         ASSERT_TRUE(IsInstancedTest());
449         MultiDrawTest::SetUp();
450     }
451 };
452 
453 // glMultiDraw*ANGLE are emulated and should always be available
TEST_P(MultiDrawTest,RequestExtension)454 TEST_P(MultiDrawTest, RequestExtension)
455 {
456     EXPECT_TRUE(requestMultiDrawExtension());
457 }
458 
459 // Test that compile a program with the extension succeeds
TEST_P(MultiDrawTest,CanCompile)460 TEST_P(MultiDrawTest, CanCompile)
461 {
462     ANGLE_SKIP_TEST_IF(!requestExtensions());
463     SetupProgram();
464 }
465 
466 // Tests basic functionality of glMultiDrawArraysANGLE
TEST_P(MultiDrawTest,MultiDrawArrays)467 TEST_P(MultiDrawTest, MultiDrawArrays)
468 {
469     ANGLE_SKIP_TEST_IF(!requestExtensions());
470 
471     // http://anglebug.com/5265
472     ANGLE_SKIP_TEST_IF(IsInstancedTest() && IsOSX() && IsIntelUHD630Mobile() && IsDesktopOpenGL());
473 
474     SetupBuffers();
475     SetupProgram();
476     DoDrawArrays();
477     EXPECT_GL_NO_ERROR();
478     CheckDrawResult();
479 }
480 
481 // Tests basic functionality of glMultiDrawElementsANGLE
TEST_P(MultiDrawTest,MultiDrawElements)482 TEST_P(MultiDrawTest, MultiDrawElements)
483 {
484     ANGLE_SKIP_TEST_IF(!requestExtensions());
485     SetupBuffers();
486     SetupProgram();
487     DoDrawElements();
488     EXPECT_GL_NO_ERROR();
489     CheckDrawResult();
490 }
491 
492 // Check that glMultiDraw*Instanced without instancing support results in GL_INVALID_OPERATION
TEST_P(MultiDrawNoInstancingSupportTest,InvalidOperation)493 TEST_P(MultiDrawNoInstancingSupportTest, InvalidOperation)
494 {
495     ANGLE_SKIP_TEST_IF(IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
496     requestMultiDrawExtension();
497     SetupBuffers();
498     SetupProgram();
499 
500     GLint first       = 0;
501     GLsizei count     = 3;
502     GLvoid *indices   = 0;
503     GLsizei instances = 1;
504 
505     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
506     glBindBuffer(GL_ARRAY_BUFFER, mNonIndexedVertexBuffer);
507     glEnableVertexAttribArray(mPositionLoc);
508     glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
509     glMultiDrawArraysInstancedANGLE(GL_TRIANGLES, &first, &count, &instances, 1);
510     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
511 
512     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
513     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
514     glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
515     glEnableVertexAttribArray(mPositionLoc);
516     glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
517     glMultiDrawElementsInstancedANGLE(GL_TRIANGLES, &count, GL_UNSIGNED_SHORT, &indices, &instances,
518                                       1);
519     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
520 }
521 
522 const angle::PlatformParameters platforms[] = {
523     ES2_D3D9(),  ES2_OPENGL(), ES2_OPENGLES(), ES2_VULKAN(),
524     ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES(),
525 };
526 
527 const angle::PlatformParameters es2_platforms[] = {
528     ES2_D3D9(),
529     ES2_OPENGL(),
530     ES2_OPENGLES(),
531     ES2_VULKAN(),
532 };
533 
534 INSTANTIATE_TEST_SUITE_P(
535     ,
536     MultiDrawTest,
537     testing::Combine(
538         testing::ValuesIn(::angle::FilterTestParams(platforms, ArraySize(platforms))),
539         testing::Values(DrawIDOption::NoDrawID, DrawIDOption::UseDrawID),
540         testing::Values(InstancingOption::NoInstancing, InstancingOption::UseInstancing),
541         testing::Values(BufferDataUsageOption::StaticDraw, BufferDataUsageOption::DynamicDraw)),
542     PrintToStringParamName());
543 
544 INSTANTIATE_TEST_SUITE_P(
545     ,
546     MultiDrawNoInstancingSupportTest,
547     testing::Combine(
548         testing::ValuesIn(::angle::FilterTestParams(es2_platforms, ArraySize(es2_platforms))),
549         testing::Values(DrawIDOption::NoDrawID, DrawIDOption::UseDrawID),
550         testing::Values(InstancingOption::UseInstancing),
551         testing::Values(BufferDataUsageOption::StaticDraw, BufferDataUsageOption::DynamicDraw)),
552     PrintToStringParamName());
553 
554 }  // namespace
555