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