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