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 // MultiDrawIndirectTest: Tests of GL_EXT_multi_draw_indirect
9
10 #include "test_utils/ANGLETest.h"
11 #include "test_utils/gl_raii.h"
12
13 using namespace angle;
14
15 namespace
16 {
17
18 // Create a kWidth * kHeight canvas equally split into kCountX * kCountY tiles
19 // each containing a quad partially covering each tile
20 constexpr uint32_t kWidth = 256;
21 constexpr uint32_t kHeight = 256;
22 constexpr uint32_t kCountX = 8;
23 constexpr uint32_t kCountY = 8;
24 constexpr uint32_t kQuadCount = kCountX * kCountY;
25 constexpr uint32_t kTriCount = kQuadCount * 2;
26 constexpr std::array<GLfloat, 2> kTileSize = {
27 1.f / static_cast<GLfloat>(kCountX),
28 1.f / static_cast<GLfloat>(kCountY),
29 };
30 constexpr std::array<uint32_t, 2> kTilePixelSize = {kWidth / kCountX, kHeight / kCountY};
31 constexpr std::array<GLfloat, 2> kQuadRadius = {0.25f * kTileSize[0], 0.25f * kTileSize[1]};
32 constexpr std::array<uint32_t, 2> kPixelCheckSize = {
33 static_cast<uint32_t>(kQuadRadius[0] * kWidth),
34 static_cast<uint32_t>(kQuadRadius[1] * kHeight)};
35
getTileCenter(uint32_t x,uint32_t y)36 constexpr std::array<GLfloat, 2> getTileCenter(uint32_t x, uint32_t y)
37 {
38 return {
39 kTileSize[0] * (0.5f + static_cast<GLfloat>(x)),
40 kTileSize[1] * (0.5f + static_cast<GLfloat>(y)),
41 };
42 }
getQuadVertices(uint32_t x,uint32_t y)43 constexpr std::array<std::array<GLfloat, 3>, 4> getQuadVertices(uint32_t x, uint32_t y)
44 {
45 const auto center = getTileCenter(x, y);
46 return {
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 std::array<GLfloat, 3>{center[0] - kQuadRadius[0], center[1] + kQuadRadius[1], 0.0f},
51 };
52 }
53
54 enum class DrawIDOption
55 {
56 NoDrawID,
57 UseDrawID,
58 };
59
60 enum class InstancingOption
61 {
62 NoInstancing,
63 UseInstancing,
64 };
65
66 enum class BufferDataUsageOption
67 {
68 StaticDraw,
69 DynamicDraw
70 };
71
72 using MultiDrawTestParams =
73 std::tuple<angle::PlatformParameters, DrawIDOption, InstancingOption, BufferDataUsageOption>;
74
75 using MultiDrawIndirectTestParams = angle::PlatformParameters;
76
77 struct PrintToStringParamName
78 {
operator ()__anonff24a2230111::PrintToStringParamName79 std::string operator()(const ::testing::TestParamInfo<MultiDrawTestParams> &info) const
80 {
81 ::std::stringstream ss;
82 ss << std::get<0>(info.param)
83 << (std::get<3>(info.param) == BufferDataUsageOption::StaticDraw ? "__StaticDraw"
84 : "__DynamicDraw")
85 << (std::get<2>(info.param) == InstancingOption::UseInstancing ? "__Instanced" : "")
86 << (std::get<1>(info.param) == DrawIDOption::UseDrawID ? "__DrawID" : "");
87 return ss.str();
88 }
89 };
90
91 struct DrawArraysIndirectCommand
92 {
DrawArraysIndirectCommand__anonff24a2230111::DrawArraysIndirectCommand93 DrawArraysIndirectCommand() : count(0u), instanceCount(0u), first(0u), baseInstance(0u) {}
DrawArraysIndirectCommand__anonff24a2230111::DrawArraysIndirectCommand94 DrawArraysIndirectCommand(GLuint count, GLuint instanceCount, GLuint first, GLuint baseInstance)
95 : count(count), instanceCount(instanceCount), first(first), baseInstance(baseInstance)
96 {}
97 GLuint count;
98 GLuint instanceCount;
99 GLuint first;
100 GLuint baseInstance;
101 };
102
103 struct DrawElementsIndirectCommand
104 {
DrawElementsIndirectCommand__anonff24a2230111::DrawElementsIndirectCommand105 DrawElementsIndirectCommand()
106 : count(0), primCount(0), firstIndex(0), baseVertex(0), baseInstance(0)
107 {}
DrawElementsIndirectCommand__anonff24a2230111::DrawElementsIndirectCommand108 DrawElementsIndirectCommand(GLuint count,
109 GLuint primCount,
110 GLuint firstIndex,
111 GLint baseVertex,
112 GLuint baseInstance)
113 : count(count),
114 primCount(primCount),
115 firstIndex(firstIndex),
116 baseVertex(baseVertex),
117 baseInstance(baseInstance)
118 {}
119 GLuint count;
120 GLuint primCount;
121 GLuint firstIndex;
122 GLint baseVertex;
123 GLuint baseInstance;
124 };
125
126 // The tests in MultiDrawTest and MultiDrawNoInstancingSupportTest check the correctness
127 // of the ANGLE_multi_draw extension.
128 // An array of quads is drawn across the screen.
129 // gl_DrawID is checked by using it to select the color of the draw.
130 // MultiDraw*Instanced entrypoints use the existing instancing APIs which are
131 // more fully tested in InstancingTest.cpp.
132 // Correct interaction with the instancing APIs is tested here by using scaling
133 // and then instancing the array of quads over four quadrants on the screen.
134 class MultiDrawTest : public ANGLETestBase, public ::testing::TestWithParam<MultiDrawTestParams>
135 {
136 protected:
MultiDrawTest()137 MultiDrawTest()
138 : ANGLETestBase(std::get<0>(GetParam())),
139 mNonIndexedVertexBuffer(0u),
140 mVertexBuffer(0u),
141 mIndexBuffer(0u),
142 mInstanceBuffer(0u),
143 mProgram(0u),
144 mPositionLoc(0u),
145 mInstanceLoc(0u)
146 {
147 setWindowWidth(kWidth);
148 setWindowHeight(kHeight);
149 setConfigRedBits(8);
150 setConfigGreenBits(8);
151 setConfigBlueBits(8);
152 setConfigAlphaBits(8);
153 }
154
SetUp()155 void SetUp() override { ANGLETestBase::ANGLETestSetUp(); }
156
IsDrawIDTest() const157 bool IsDrawIDTest() const { return std::get<1>(GetParam()) == DrawIDOption::UseDrawID; }
158
IsInstancedTest() const159 bool IsInstancedTest() const
160 {
161 return std::get<2>(GetParam()) == InstancingOption::UseInstancing;
162 }
163
getBufferDataUsage() const164 GLenum getBufferDataUsage() const
165 {
166 return std::get<3>(GetParam()) == BufferDataUsageOption::StaticDraw ? GL_STATIC_DRAW
167 : GL_DYNAMIC_DRAW;
168 }
169
VertexShaderSource()170 std::string VertexShaderSource()
171 {
172
173 std::stringstream shader;
174 shader << (IsDrawIDTest() ? "#extension GL_ANGLE_multi_draw : require\n" : "")
175 << (IsInstancedTest() ? "attribute float vInstance;" : "") << R"(
176 attribute vec2 vPosition;
177 varying vec4 color;
178 void main()
179 {
180 int id = )" << (IsDrawIDTest() ? "gl_DrawID" : "0")
181 << ";"
182 << R"(
183 float quad_id = float(id / 2);
184 float color_id = quad_id - (3.0 * floor(quad_id / 3.0));
185 if (color_id == 0.0) {
186 color = vec4(1, 0, 0, 1);
187 } else if (color_id == 1.0) {
188 color = vec4(0, 1, 0, 1);
189 } else {
190 color = vec4(0, 0, 1, 1);
191 }
192
193 mat3 transform = mat3(1.0);
194 )"
195 << (IsInstancedTest() ? R"(
196 transform[0][0] = 0.5;
197 transform[1][1] = 0.5;
198 if (vInstance == 0.0) {
199
200 } else if (vInstance == 1.0) {
201 transform[2][0] = 0.5;
202 } else if (vInstance == 2.0) {
203 transform[2][1] = 0.5;
204 } else if (vInstance == 3.0) {
205 transform[2][0] = 0.5;
206 transform[2][1] = 0.5;
207 }
208 )"
209 : "")
210 << R"(
211 gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
212 })";
213
214 return shader.str();
215 }
216
FragmentShaderSource()217 std::string FragmentShaderSource()
218 {
219 return
220 R"(precision mediump float;
221 varying vec4 color;
222 void main()
223 {
224 gl_FragColor = color;
225 })";
226 }
227
SetupProgram()228 void SetupProgram()
229 {
230 mProgram = CompileProgram(VertexShaderSource().c_str(), FragmentShaderSource().c_str());
231 EXPECT_GL_NO_ERROR();
232 ASSERT_GE(mProgram, 1u);
233 glUseProgram(mProgram);
234 mPositionLoc = glGetAttribLocation(mProgram, "vPosition");
235 mInstanceLoc = glGetAttribLocation(mProgram, "vInstance");
236 }
237
SetupBuffers()238 void SetupBuffers()
239 {
240 for (uint32_t y = 0; y < kCountY; ++y)
241 {
242 for (uint32_t x = 0; x < kCountX; ++x)
243 {
244 // v3 ---- v2
245 // | |
246 // | |
247 // v0 ---- v1
248 uint32_t quadIndex = y * kCountX + x;
249 GLushort starting_index = static_cast<GLushort>(4 * quadIndex);
250 std::array<GLushort, 6> is = {0, 1, 2, 0, 2, 3};
251 const auto vs = getQuadVertices(x, y);
252 for (GLushort i : is)
253 {
254 mIndices.push_back(starting_index + i);
255 }
256
257 for (const auto &v : vs)
258 {
259 mVertices.insert(mVertices.end(), v.begin(), v.end());
260 }
261
262 for (GLushort i : is)
263 {
264 mNonIndexedVertices.insert(mNonIndexedVertices.end(), vs[i].begin(),
265 vs[i].end());
266 }
267 }
268 }
269
270 std::array<GLfloat, 4> instances{0, 1, 2, 3};
271
272 glGenBuffers(1, &mNonIndexedVertexBuffer);
273 glBindBuffer(GL_ARRAY_BUFFER, mNonIndexedVertexBuffer);
274 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mNonIndexedVertices.size(),
275 mNonIndexedVertices.data(), getBufferDataUsage());
276
277 glGenBuffers(1, &mVertexBuffer);
278 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
279 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mVertices.size(), mVertices.data(),
280 getBufferDataUsage());
281
282 glGenBuffers(1, &mIndexBuffer);
283 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
284 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * mIndices.size(), mIndices.data(),
285 getBufferDataUsage());
286
287 glGenBuffers(1, &mInstanceBuffer);
288 glBindBuffer(GL_ARRAY_BUFFER, mInstanceBuffer);
289 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * instances.size(), instances.data(),
290 getBufferDataUsage());
291
292 ASSERT_GL_NO_ERROR();
293 }
294
DoVertexAttribDivisor(GLint location,GLuint divisor)295 void DoVertexAttribDivisor(GLint location, GLuint divisor)
296 {
297 if (getClientMajorVersion() <= 2)
298 {
299 ASSERT_TRUE(IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
300 glVertexAttribDivisorANGLE(location, divisor);
301 }
302 else
303 {
304 glVertexAttribDivisor(location, divisor);
305 }
306 }
307
DoDrawArrays()308 void DoDrawArrays()
309 {
310 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
311 glBindBuffer(GL_ARRAY_BUFFER, mNonIndexedVertexBuffer);
312 glEnableVertexAttribArray(mPositionLoc);
313 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
314
315 std::vector<GLint> firsts(kTriCount);
316 std::vector<GLsizei> counts(kTriCount, 3);
317 for (uint32_t i = 0; i < kTriCount; ++i)
318 firsts[i] = i * 3;
319
320 if (IsInstancedTest())
321 {
322 glBindBuffer(GL_ARRAY_BUFFER, mInstanceBuffer);
323 glEnableVertexAttribArray(mInstanceLoc);
324 glVertexAttribPointer(mInstanceLoc, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
325 DoVertexAttribDivisor(mInstanceLoc, 1);
326 std::vector<GLsizei> instanceCounts(kTriCount, 4);
327 glMultiDrawArraysInstancedANGLE(GL_TRIANGLES, firsts.data(), counts.data(),
328 instanceCounts.data(), kTriCount);
329 }
330 else
331 {
332 glMultiDrawArraysANGLE(GL_TRIANGLES, firsts.data(), counts.data(), kTriCount);
333 }
334 }
335
DoDrawElements()336 void DoDrawElements()
337 {
338 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
339 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
340 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
341 glEnableVertexAttribArray(mPositionLoc);
342 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
343
344 std::vector<GLsizei> counts(kTriCount, 3);
345 std::vector<const GLvoid *> indices(kTriCount);
346 for (uint32_t i = 0; i < kTriCount; ++i)
347 indices[i] = reinterpret_cast<GLvoid *>(static_cast<uintptr_t>(i * 3 * 2));
348
349 if (IsInstancedTest())
350 {
351 glBindBuffer(GL_ARRAY_BUFFER, mInstanceBuffer);
352 glEnableVertexAttribArray(mInstanceLoc);
353 glVertexAttribPointer(mInstanceLoc, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
354 DoVertexAttribDivisor(mInstanceLoc, 1);
355 std::vector<GLsizei> instanceCounts(kTriCount, 4);
356 glMultiDrawElementsInstancedANGLE(GL_TRIANGLES, counts.data(), GL_UNSIGNED_SHORT,
357 indices.data(), instanceCounts.data(), kTriCount);
358 }
359 else
360 {
361 glMultiDrawElementsANGLE(GL_TRIANGLES, counts.data(), GL_UNSIGNED_SHORT, indices.data(),
362 kTriCount);
363 }
364 }
365
CheckDrawResult()366 void CheckDrawResult()
367 {
368 for (uint32_t y = 0; y < kCountY; ++y)
369 {
370 for (uint32_t x = 0; x < kCountX; ++x)
371 {
372 uint32_t center_x = x * kTilePixelSize[0] + kTilePixelSize[0] / 2;
373 uint32_t center_y = y * kTilePixelSize[1] + kTilePixelSize[1] / 2;
374 uint32_t quadID = IsDrawIDTest() ? y * kCountX + x : 0;
375 uint32_t colorID = quadID % 3u;
376 std::array<GLColor, 3> colors = {GLColor(255, 0, 0, 255), GLColor(0, 255, 0, 255),
377 GLColor(0, 0, 255, 255)};
378 GLColor expected = colors[colorID];
379
380 if (IsInstancedTest())
381 {
382 EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4,
383 center_y / 2 - kPixelCheckSize[1] / 4,
384 kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
385 EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4 + kWidth / 2,
386 center_y / 2 - kPixelCheckSize[1] / 4,
387 kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
388 EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4,
389 center_y / 2 - kPixelCheckSize[1] / 4 + kHeight / 2,
390 kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
391 EXPECT_PIXEL_RECT_EQ(center_x / 2 - kPixelCheckSize[0] / 4 + kWidth / 2,
392 center_y / 2 - kPixelCheckSize[1] / 4 + kHeight / 2,
393 kPixelCheckSize[0] / 2, kPixelCheckSize[1] / 2, expected);
394 }
395 else
396 {
397 EXPECT_PIXEL_RECT_EQ(center_x - kPixelCheckSize[0] / 2,
398 center_y - kPixelCheckSize[1] / 2, kPixelCheckSize[0],
399 kPixelCheckSize[1], expected);
400 }
401 }
402 }
403 }
404
TearDown()405 void TearDown() override
406 {
407 if (mNonIndexedVertexBuffer != 0u)
408 {
409 glDeleteBuffers(1, &mNonIndexedVertexBuffer);
410 }
411 if (mVertexBuffer != 0u)
412 {
413 glDeleteBuffers(1, &mVertexBuffer);
414 }
415 if (mIndexBuffer != 0u)
416 {
417 glDeleteBuffers(1, &mIndexBuffer);
418 }
419 if (mInstanceBuffer != 0u)
420 {
421 glDeleteBuffers(1, &mInstanceBuffer);
422 }
423 if (mProgram != 0)
424 {
425 glDeleteProgram(mProgram);
426 }
427 ANGLETestBase::ANGLETestTearDown();
428 }
429
requestMultiDrawExtension()430 bool requestMultiDrawExtension() { return EnsureGLExtensionEnabled("GL_ANGLE_multi_draw"); }
431
requestInstancedExtension()432 bool requestInstancedExtension()
433 {
434 return EnsureGLExtensionEnabled("GL_ANGLE_instanced_arrays");
435 }
436
requestExtensions()437 bool requestExtensions()
438 {
439 if (IsInstancedTest() && getClientMajorVersion() <= 2)
440 {
441 if (!requestInstancedExtension())
442 {
443 return false;
444 }
445 }
446 return requestMultiDrawExtension();
447 }
448
449 std::vector<GLushort> mIndices;
450 std::vector<GLfloat> mVertices;
451 std::vector<GLfloat> mNonIndexedVertices;
452 GLuint mNonIndexedVertexBuffer;
453 GLuint mVertexBuffer;
454 GLuint mIndexBuffer;
455 GLuint mInstanceBuffer;
456 GLuint mProgram;
457 GLint mPositionLoc;
458 GLint mInstanceLoc;
459 };
460
461 class MultiDrawNoInstancingSupportTest : public MultiDrawTest
462 {
SetUp()463 void SetUp() override
464 {
465 ASSERT_LE(getClientMajorVersion(), 2);
466 ASSERT_TRUE(IsInstancedTest());
467 MultiDrawTest::SetUp();
468 }
469 };
470
471 // The tests in MultiDrawIndirectTest check the correctness
472 // of the EXT_multi_draw_indirect extension.
473 // 4 magenta triangles are drawn at the corners of the screen
474 // in different orders from the same vertex and index arrays.
475 class MultiDrawIndirectTest : public ANGLETestBase,
476 public ::testing::TestWithParam<MultiDrawIndirectTestParams>
477 {
478 protected:
MultiDrawIndirectTest()479 MultiDrawIndirectTest()
480 : ANGLETestBase(GetParam()),
481 mPositionLoc(0u),
482 mVertexBuffer(0u),
483 mIndexBuffer(0u),
484 mIndirectBuffer(0u),
485 mProgram(0u)
486 {
487 setWindowWidth(kWidth);
488 setWindowHeight(kHeight);
489 setConfigRedBits(8);
490 setConfigGreenBits(8);
491 setConfigBlueBits(8);
492 setConfigAlphaBits(8);
493 }
494
SetUp()495 void SetUp() override { ANGLETestBase::ANGLETestSetUp(); }
496
SetupProgramIndirect()497 void SetupProgramIndirect()
498 {
499 // Define the vertex and fragment shaders
500 constexpr char kVS[] = R"(#version 310 es
501 in vec3 aPos;
502 void main()
503 {
504 gl_Position = vec4(aPos.x, aPos.y, 0, 1);
505 })";
506
507 constexpr char kFS[] = R"(#version 310 es
508 precision mediump float;
509 out vec4 colorOut;
510 void main()
511 {
512 colorOut = vec4(1.0, 0.0, 1.0, 1.0);
513 })";
514 mProgram = CompileProgram(kVS, kFS);
515 EXPECT_GL_NO_ERROR();
516 ASSERT_GE(mProgram, 1u);
517 glUseProgram(mProgram);
518 mPositionLoc = glGetAttribLocation(mProgram, "aPos");
519 }
520
TearDown()521 void TearDown() override
522 {
523 if (mVertexBuffer != 0u)
524 {
525 glDeleteBuffers(1, &mVertexBuffer);
526 }
527 if (mIndexBuffer != 0u)
528 {
529 glDeleteBuffers(1, &mIndexBuffer);
530 }
531 if (mProgram != 0)
532 {
533 glDeleteProgram(mProgram);
534 }
535 if (mIndirectBuffer != 0u)
536 {
537 glDeleteBuffers(1, &mIndirectBuffer);
538 }
539 ANGLETestBase::ANGLETestTearDown();
540 }
541
542 GLint mPositionLoc;
543 GLuint mVertexBuffer;
544 GLuint mIndexBuffer;
545 GLuint mIndirectBuffer;
546 GLuint mProgram;
547 };
548
549 // glMultiDraw*ANGLE are emulated and should always be available
TEST_P(MultiDrawTest,RequestExtension)550 TEST_P(MultiDrawTest, RequestExtension)
551 {
552 EXPECT_TRUE(requestMultiDrawExtension());
553 }
554
555 // Test that compile a program with the extension succeeds
TEST_P(MultiDrawTest,CanCompile)556 TEST_P(MultiDrawTest, CanCompile)
557 {
558 ANGLE_SKIP_TEST_IF(!requestExtensions());
559 SetupProgram();
560 }
561
562 // Tests basic functionality of glMultiDrawArraysANGLE
TEST_P(MultiDrawTest,MultiDrawArrays)563 TEST_P(MultiDrawTest, MultiDrawArrays)
564 {
565 ANGLE_SKIP_TEST_IF(!requestExtensions());
566
567 // http://anglebug.com/5265
568 ANGLE_SKIP_TEST_IF(IsInstancedTest() && IsOSX() && IsIntelUHD630Mobile() && IsDesktopOpenGL());
569
570 SetupBuffers();
571 SetupProgram();
572 DoDrawArrays();
573 EXPECT_GL_NO_ERROR();
574 CheckDrawResult();
575 }
576
577 // Tests basic functionality of glMultiDrawElementsANGLE
TEST_P(MultiDrawTest,MultiDrawElements)578 TEST_P(MultiDrawTest, MultiDrawElements)
579 {
580 ANGLE_SKIP_TEST_IF(!requestExtensions());
581 SetupBuffers();
582 SetupProgram();
583 DoDrawElements();
584 EXPECT_GL_NO_ERROR();
585 CheckDrawResult();
586 }
587
588 // Tests basic functionality of glMultiDrawArraysIndirectEXT
TEST_P(MultiDrawIndirectTest,MultiDrawArraysIndirect)589 TEST_P(MultiDrawIndirectTest, MultiDrawArraysIndirect)
590 {
591 ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multi_draw_indirect"));
592
593 // Set up the vertex array
594 const GLint triangleCount = 4;
595 const std::vector<GLfloat> vertices = {
596 -1, 1, 0, -1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0,
597 -1, -1, 0, -1, 0, 0, 0, -1, 0, 1, -1, 0, 0, -1, 0, 1, 0, 0,
598 };
599
600 // Set up the vertex buffer
601 GLVertexArray vao;
602 glBindVertexArray(vao);
603
604 glGenBuffers(1, &mVertexBuffer);
605 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
606 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertices.size(), vertices.data(),
607 GL_STATIC_DRAW);
608 EXPECT_GL_NO_ERROR();
609
610 // Generate program
611 SetupProgramIndirect();
612
613 // Set up the vertex array format
614 glEnableVertexAttribArray(mPositionLoc);
615 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
616 EXPECT_GL_NO_ERROR();
617
618 // Set up the indirect data array
619 std::array<DrawArraysIndirectCommand, triangleCount> indirectData;
620 const GLsizei icSize = sizeof(DrawArraysIndirectCommand);
621 for (auto i = 0; i < triangleCount; i++)
622 {
623 indirectData[i] = DrawArraysIndirectCommand(3, 1, 3 * i, i);
624 }
625
626 glGenBuffers(1, &mIndirectBuffer);
627 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, mIndirectBuffer);
628 glBufferData(GL_DRAW_INDIRECT_BUFFER, icSize * indirectData.size(), indirectData.data(),
629 GL_STATIC_DRAW);
630 EXPECT_GL_NO_ERROR();
631
632 // Invalid value check for drawcount and stride
633 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
634 glMultiDrawArraysIndirectEXT(GL_TRIANGLES, nullptr, 0, 0);
635 EXPECT_GL_ERROR(GL_INVALID_VALUE);
636 glMultiDrawArraysIndirectEXT(GL_TRIANGLES, nullptr, -1, 0);
637 EXPECT_GL_ERROR(GL_INVALID_VALUE);
638 glMultiDrawArraysIndirectEXT(GL_TRIANGLES, nullptr, 1, 2);
639 EXPECT_GL_ERROR(GL_INVALID_VALUE);
640
641 // Check the error from sourcing beyond the allocated buffer size
642 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
643 glMultiDrawArraysIndirectEXT(
644 GL_TRIANGLES,
645 reinterpret_cast<const void *>(static_cast<uintptr_t>(icSize * triangleCount)), 1, 0);
646 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
647
648 // Draw all triangles using glMultiDrawArraysIndirect
649 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
650 glMultiDrawArraysIndirectEXT(GL_TRIANGLES, nullptr, triangleCount, 0);
651 EXPECT_GL_NO_ERROR();
652
653 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
654 EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::magenta);
655 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::magenta);
656 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::magenta);
657 EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::transparentBlack);
658
659 // Draw the triangles in different order
660 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
661 glMultiDrawArraysIndirectEXT(GL_TRIANGLES, nullptr, 1, 0);
662 glMultiDrawArraysIndirectEXT(GL_TRIANGLES,
663 reinterpret_cast<const void *>(static_cast<uintptr_t>(icSize * 2)),
664 triangleCount - 2, 0);
665 glMultiDrawArraysIndirectEXT(
666 GL_TRIANGLES, reinterpret_cast<const void *>(static_cast<uintptr_t>(icSize)), 1, 0);
667 EXPECT_GL_NO_ERROR();
668
669 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
670 EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::magenta);
671 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::magenta);
672 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::magenta);
673 EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::transparentBlack);
674
675 // Draw the triangles partially using stride
676 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
677 glMultiDrawArraysIndirectEXT(GL_TRIANGLES, nullptr, 2, icSize * 3);
678 EXPECT_GL_NO_ERROR();
679
680 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::transparentBlack);
681 EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::magenta);
682 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::magenta);
683 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::transparentBlack);
684 EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::transparentBlack);
685 }
686
687 // Tests basic functionality of glMultiDrawElementsIndirectEXT
TEST_P(MultiDrawIndirectTest,MultiDrawElementsIndirect)688 TEST_P(MultiDrawIndirectTest, MultiDrawElementsIndirect)
689 {
690 ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multi_draw_indirect"));
691
692 // Set up the vertex array
693 const GLint triangleCount = 4;
694 const std::vector<GLfloat> vertices = {
695 -1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, -1, 0, 0, -1, 0, -1, -1, 0, -1, 0, 0,
696 };
697 const std::vector<GLuint> indices = {
698 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 0, 1,
699 };
700
701 // Set up the vertex and index buffers
702 GLVertexArray vao;
703 glBindVertexArray(vao);
704
705 glGenBuffers(1, &mVertexBuffer);
706 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
707 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertices.size(), vertices.data(),
708 GL_STATIC_DRAW);
709
710 glGenBuffers(1, &mIndexBuffer);
711 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
712 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), indices.data(),
713 GL_STATIC_DRAW);
714 EXPECT_GL_NO_ERROR();
715
716 // Generate program
717 SetupProgramIndirect();
718
719 // Set up the vertex array format
720 glEnableVertexAttribArray(mPositionLoc);
721 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
722 EXPECT_GL_NO_ERROR();
723
724 // Set up the indirect data array
725 std::array<DrawElementsIndirectCommand, triangleCount> indirectData;
726 const GLsizei icSize = sizeof(DrawElementsIndirectCommand);
727 for (auto i = 0; i < triangleCount; i++)
728 {
729 indirectData[i] = DrawElementsIndirectCommand(3, 1, 3 * i, 0, i);
730 }
731
732 glGenBuffers(1, &mIndirectBuffer);
733 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, mIndirectBuffer);
734 glBufferData(GL_DRAW_INDIRECT_BUFFER, icSize * indirectData.size(), indirectData.data(),
735 GL_STATIC_DRAW);
736 EXPECT_GL_NO_ERROR();
737
738 // Invalid value check for drawcount and stride
739 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
740 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, 0, 0);
741 EXPECT_GL_ERROR(GL_INVALID_VALUE);
742 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, -1, 0);
743 EXPECT_GL_ERROR(GL_INVALID_VALUE);
744 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, 1, 2);
745 EXPECT_GL_ERROR(GL_INVALID_VALUE);
746
747 // Check the error from sourcing beyond the allocated buffer size
748 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
749 glMultiDrawElementsIndirectEXT(
750 GL_TRIANGLES, GL_UNSIGNED_INT,
751 reinterpret_cast<const void *>(static_cast<uintptr_t>(icSize * triangleCount)), 1, 0);
752 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
753
754 // Draw all triangles using glMultiDrawElementsIndirect
755 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
756 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, triangleCount, 0);
757 EXPECT_GL_NO_ERROR();
758
759 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
760 EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::magenta);
761 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::magenta);
762 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::magenta);
763 EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::transparentBlack);
764
765 // Draw the triangles in a different order
766 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
767 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, 1, 0);
768 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT,
769 reinterpret_cast<const void *>(static_cast<uintptr_t>(icSize)),
770 triangleCount - 2, 0);
771 glMultiDrawElementsIndirectEXT(
772 GL_TRIANGLES, GL_UNSIGNED_INT,
773 reinterpret_cast<const void *>(static_cast<uintptr_t>(icSize * 3)), 1, 0);
774 EXPECT_GL_NO_ERROR();
775
776 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
777 EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::magenta);
778 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::magenta);
779 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::magenta);
780 EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::transparentBlack);
781
782 // Draw the triangles partially using stride
783 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
784 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, 2, icSize * 3);
785 EXPECT_GL_NO_ERROR();
786
787 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::transparentBlack);
788 EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::magenta);
789 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::transparentBlack);
790 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::magenta);
791 EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::transparentBlack);
792 }
793
794 // Tests functionality of glMultiDrawElementsIndirectEXT with more than one triangle in one element
795 // of the indirect buffer.
TEST_P(MultiDrawIndirectTest,MultiDrawElementsIndirectMultipleTriangles)796 TEST_P(MultiDrawIndirectTest, MultiDrawElementsIndirectMultipleTriangles)
797 {
798 ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multi_draw_indirect"));
799
800 // Set up the vertex array
801 const GLint triangleCount = 4;
802 const std::vector<GLfloat> vertices = {
803 -1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, -1, 0, 0, -1, 0, -1, -1, 0, -1, 0, 0,
804 };
805 const std::vector<GLuint> indices = {
806 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 0, 1,
807 };
808
809 // Set up the vertex and index buffers
810 GLVertexArray vao;
811 glBindVertexArray(vao);
812
813 glGenBuffers(1, &mVertexBuffer);
814 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
815 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertices.size(), vertices.data(),
816 GL_STATIC_DRAW);
817
818 glGenBuffers(1, &mIndexBuffer);
819 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
820 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), indices.data(),
821 GL_STATIC_DRAW);
822 EXPECT_GL_NO_ERROR();
823
824 // Generate program
825 SetupProgramIndirect();
826
827 // Set up the vertex array format
828 glEnableVertexAttribArray(mPositionLoc);
829 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
830 EXPECT_GL_NO_ERROR();
831
832 // Set up the indirect data array; first element represents two triangles.
833 std::array<DrawElementsIndirectCommand, triangleCount - 1> indirectData;
834 const GLsizei icSize = sizeof(DrawElementsIndirectCommand);
835 for (auto i = 0; i < triangleCount - 1; i++)
836 {
837 if (i == 0)
838 {
839 indirectData[i] = DrawElementsIndirectCommand(6, 2, 0, 0, i);
840 }
841 else
842 {
843 indirectData[i] = DrawElementsIndirectCommand(3, 1, 3 * (i + 1), 0, i);
844 }
845 }
846
847 glGenBuffers(1, &mIndirectBuffer);
848 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, mIndirectBuffer);
849 glBufferData(GL_DRAW_INDIRECT_BUFFER, icSize * indirectData.size(), indirectData.data(),
850 GL_STATIC_DRAW);
851 EXPECT_GL_NO_ERROR();
852
853 // Draw all triangles using glMultiDrawElementsIndirect
854 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
855 glMultiDrawElementsIndirectEXT(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, triangleCount - 1, 0);
856 EXPECT_GL_NO_ERROR();
857
858 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
859 EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::magenta);
860 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::magenta);
861 EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::magenta);
862 EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::transparentBlack);
863 }
864
865 // Check that glMultiDraw*Instanced without instancing support results in GL_INVALID_OPERATION
TEST_P(MultiDrawNoInstancingSupportTest,InvalidOperation)866 TEST_P(MultiDrawNoInstancingSupportTest, InvalidOperation)
867 {
868 ANGLE_SKIP_TEST_IF(IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
869 requestMultiDrawExtension();
870 SetupBuffers();
871 SetupProgram();
872
873 GLint first = 0;
874 GLsizei count = 3;
875 GLvoid *indices = nullptr;
876 GLsizei instances = 1;
877
878 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
879 glBindBuffer(GL_ARRAY_BUFFER, mNonIndexedVertexBuffer);
880 glEnableVertexAttribArray(mPositionLoc);
881 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
882 glMultiDrawArraysInstancedANGLE(GL_TRIANGLES, &first, &count, &instances, 1);
883 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
884
885 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
886 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
887 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
888 glEnableVertexAttribArray(mPositionLoc);
889 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
890 glMultiDrawElementsInstancedANGLE(GL_TRIANGLES, &count, GL_UNSIGNED_SHORT, &indices, &instances,
891 1);
892 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
893 }
894
895 const angle::PlatformParameters platforms[] = {
896 ES2_D3D9(), ES2_OPENGL(), ES2_OPENGLES(), ES2_VULKAN(),
897 ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES(),
898 };
899
900 const angle::PlatformParameters es2_platforms[] = {
901 ES2_D3D9(),
902 ES2_OPENGL(),
903 ES2_OPENGLES(),
904 ES2_VULKAN(),
905 };
906
907 INSTANTIATE_TEST_SUITE_P(
908 ,
909 MultiDrawTest,
910 testing::Combine(
911 testing::ValuesIn(::angle::FilterTestParams(platforms, ArraySize(platforms))),
912 testing::Values(DrawIDOption::NoDrawID, DrawIDOption::UseDrawID),
913 testing::Values(InstancingOption::NoInstancing, InstancingOption::UseInstancing),
914 testing::Values(BufferDataUsageOption::StaticDraw, BufferDataUsageOption::DynamicDraw)),
915 PrintToStringParamName());
916
917 INSTANTIATE_TEST_SUITE_P(
918 ,
919 MultiDrawNoInstancingSupportTest,
920 testing::Combine(
921 testing::ValuesIn(::angle::FilterTestParams(es2_platforms, ArraySize(es2_platforms))),
922 testing::Values(DrawIDOption::NoDrawID, DrawIDOption::UseDrawID),
923 testing::Values(InstancingOption::UseInstancing),
924 testing::Values(BufferDataUsageOption::StaticDraw, BufferDataUsageOption::DynamicDraw)),
925 PrintToStringParamName());
926
927 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultiDrawIndirectTest);
928
929 ANGLE_INSTANTIATE_TEST_ES31_AND(MultiDrawIndirectTest,
930 WithNoVulkanMultiDrawIndirect(ES31_VULKAN()));
931 } // namespace
932