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