xref: /aosp_15_r20/external/angle/src/tests/perf_tests/DrawCallPerf.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2014 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 // DrawCallPerf:
7 //   Performance tests for ANGLE draw call overhead.
8 //
9 
10 #include "ANGLEPerfTest.h"
11 #include "DrawCallPerfParams.h"
12 #include "common/PackedEnums.h"
13 #include "test_utils/draw_call_perf_utils.h"
14 #include "util/shader_utils.h"
15 
16 using namespace angle;
17 
18 namespace
19 {
20 enum class StateChange
21 {
22     NoChange,
23     VertexAttrib,
24     VertexBuffer,
25     ManyVertexBuffers,
26     Texture,
27     Program,
28     VertexBufferCycle,
29     Scissor,
30     ManyTextureDraw,
31     Uniform,
32     InvalidEnum,
33     EnumCount = InvalidEnum,
34 };
35 
36 constexpr size_t kCycleVBOPoolSize  = 200;
37 constexpr size_t kManyTexturesCount = 8;
38 
39 struct DrawArraysPerfParams : public DrawCallPerfParams
40 {
41     DrawArraysPerfParams() = default;
DrawArraysPerfParams__anon7e99dd4c0111::DrawArraysPerfParams42     DrawArraysPerfParams(const DrawCallPerfParams &base) : DrawCallPerfParams(base) {}
43 
44     std::string story() const override;
45 
46     StateChange stateChange = StateChange::NoChange;
47 };
48 
story() const49 std::string DrawArraysPerfParams::story() const
50 {
51     std::stringstream strstr;
52 
53     strstr << DrawCallPerfParams::story();
54 
55     switch (stateChange)
56     {
57         case StateChange::VertexAttrib:
58             strstr << "_attrib_change";
59             break;
60         case StateChange::VertexBuffer:
61             strstr << "_vbo_change";
62             break;
63         case StateChange::ManyVertexBuffers:
64             strstr << "_manyvbos_change";
65             break;
66         case StateChange::Texture:
67             strstr << "_tex_change";
68             break;
69         case StateChange::Program:
70             strstr << "_prog_change";
71             break;
72         case StateChange::VertexBufferCycle:
73             strstr << "_vbo_cycle";
74             break;
75         case StateChange::Scissor:
76             strstr << "_scissor_change";
77             break;
78         case StateChange::ManyTextureDraw:
79             strstr << "_many_tex_draw";
80             break;
81         case StateChange::Uniform:
82             strstr << "_uniform";
83             break;
84         default:
85             break;
86     }
87 
88     return strstr.str();
89 }
90 
operator <<(std::ostream & os,const DrawArraysPerfParams & params)91 std::ostream &operator<<(std::ostream &os, const DrawArraysPerfParams &params)
92 {
93     os << params.backendAndStory().substr(1);
94     return os;
95 }
96 
CreateSimpleTexture2D()97 GLuint CreateSimpleTexture2D()
98 {
99     // Use tightly packed data
100     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
101 
102     // Generate a texture object
103     GLuint texture;
104     glGenTextures(1, &texture);
105 
106     // Bind the texture object
107     glBindTexture(GL_TEXTURE_2D, texture);
108 
109     // Load the texture: 2x2 Image, 3 bytes per pixel (R, G, B)
110     constexpr size_t width             = 2;
111     constexpr size_t height            = 2;
112     GLubyte pixels[width * height * 4] = {
113         255, 0,   0,   0,  // Red
114         0,   255, 0,   0,  // Green
115         0,   0,   255, 0,  // Blue
116         255, 255, 0,   0   // Yellow
117     };
118     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
119 
120     // Set the filtering mode
121     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
122     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
123 
124     return texture;
125 }
126 
127 class DrawCallPerfBenchmark : public ANGLERenderTest,
128                               public ::testing::WithParamInterface<DrawArraysPerfParams>
129 {
130   public:
131     DrawCallPerfBenchmark();
132 
133     void initializeBenchmark() override;
134     void destroyBenchmark() override;
135     void drawBenchmark() override;
136 
137   private:
138     GLuint mProgram1   = 0;
139     GLuint mProgram2   = 0;
140     GLuint mProgram3   = 0;
141     GLuint mBuffer1    = 0;
142     GLuint mBuffer2    = 0;
143     GLuint mFBO        = 0;
144     GLuint mFBOTexture = 0;
145     std::vector<GLuint> mTextures;
146     int mNumTris = GetParam().numTris;
147     std::vector<GLuint> mVBOPool;
148     size_t mCurrentVBO = 0;
149 };
150 
DrawCallPerfBenchmark()151 DrawCallPerfBenchmark::DrawCallPerfBenchmark() : ANGLERenderTest("DrawCallPerf", GetParam())
152 {
153     const auto &params = GetParam();
154     if (IsPixel6() && params.eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE &&
155         params.surfaceType == SurfaceType::Offscreen &&
156         (params.stateChange == StateChange::VertexAttrib ||
157          params.stateChange == StateChange::Program))
158     {
159         skipTest("https://issuetracker.google.com/issues/298407224 Fails on Pixel 6 GLES");
160     }
161 }
162 
initializeBenchmark()163 void DrawCallPerfBenchmark::initializeBenchmark()
164 {
165     const auto &params = GetParam();
166 
167     if (params.stateChange == StateChange::Texture)
168     {
169         mProgram1 = SetupSimpleTextureProgram();
170         ASSERT_NE(0u, mProgram1);
171     }
172     else if (params.stateChange == StateChange::ManyTextureDraw)
173     {
174         mProgram3 = SetupEightTextureProgram();
175         ASSERT_NE(0u, mProgram3);
176     }
177     else if (params.stateChange == StateChange::Program)
178     {
179         mProgram1 = SetupSimpleTextureProgram();
180         mProgram2 = SetupDoubleTextureProgram();
181         ASSERT_NE(0u, mProgram1);
182         ASSERT_NE(0u, mProgram2);
183     }
184     else if (params.stateChange == StateChange::ManyVertexBuffers)
185     {
186         constexpr char kVS[] = R"(attribute vec2 vPosition;
187 attribute vec2 v0;
188 attribute vec2 v1;
189 attribute vec2 v2;
190 attribute vec2 v3;
191 const float scale = 0.5;
192 const float offset = -0.5;
193 
194 varying vec2 v;
195 
196 void main()
197 {
198     gl_Position = vec4(vPosition * vec2(scale) + vec2(offset), 0, 1);
199     v = (v0 + v1 + v2 + v3) * 0.25;
200 })";
201 
202         constexpr char kFS[] = R"(precision mediump float;
203 varying vec2 v;
204 void main()
205 {
206     gl_FragColor = vec4(v, 0, 1);
207 })";
208 
209         mProgram1 = CompileProgram(kVS, kFS);
210         ASSERT_NE(0u, mProgram1);
211         glBindAttribLocation(mProgram1, 1, "v0");
212         glBindAttribLocation(mProgram1, 2, "v1");
213         glBindAttribLocation(mProgram1, 3, "v2");
214         glBindAttribLocation(mProgram1, 4, "v3");
215         glEnableVertexAttribArray(1);
216         glEnableVertexAttribArray(2);
217         glEnableVertexAttribArray(3);
218         glEnableVertexAttribArray(4);
219     }
220     else if (params.stateChange == StateChange::VertexBufferCycle)
221     {
222         mProgram1 = SetupSimpleDrawProgram();
223         ASSERT_NE(0u, mProgram1);
224 
225         for (size_t bufferIndex = 0; bufferIndex < kCycleVBOPoolSize; ++bufferIndex)
226         {
227             GLuint buffer = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
228             mVBOPool.push_back(buffer);
229         }
230     }
231     else if (params.stateChange == StateChange::Uniform)
232     {
233         constexpr char kVS[] = R"(attribute vec2 vPosition;
234 void main()
235 {
236     gl_Position = vec4(vPosition, 0, 1);
237 })";
238 
239         constexpr char kFS[] = R"(precision mediump float;
240 uniform vec4 uni;
241 void main()
242 {
243     gl_FragColor = uni;
244 })";
245 
246         mProgram1 = CompileProgram(kVS, kFS);
247         ASSERT_NE(0u, mProgram1);
248     }
249     else
250     {
251         mProgram1 = SetupSimpleDrawProgram();
252         ASSERT_NE(0u, mProgram1);
253     }
254 
255     // Re-link program to ensure the attrib bindings are used.
256     if (mProgram1)
257     {
258         glBindAttribLocation(mProgram1, 0, "vPosition");
259         glLinkProgram(mProgram1);
260         glUseProgram(mProgram1);
261     }
262 
263     if (mProgram2)
264     {
265         glBindAttribLocation(mProgram2, 0, "vPosition");
266         glLinkProgram(mProgram2);
267     }
268 
269     if (mProgram3)
270     {
271         glBindAttribLocation(mProgram3, 0, "vPosition");
272         glLinkProgram(mProgram3);
273     }
274 
275     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
276 
277     mBuffer1 = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
278     mBuffer2 = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
279 
280     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
281     glEnableVertexAttribArray(0);
282 
283     // Set the viewport
284     glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
285 
286     if (params.surfaceType == SurfaceType::Offscreen)
287     {
288         CreateColorFBO(getWindow()->getWidth(), getWindow()->getHeight(), &mFBOTexture, &mFBO);
289     }
290 
291     for (size_t i = 0; i < kManyTexturesCount; ++i)
292     {
293         mTextures.emplace_back(CreateSimpleTexture2D());
294     }
295 
296     if (params.stateChange == StateChange::Program)
297     {
298         // Bind the textures as appropriate, they are not modified during the test.
299         GLint program1Tex1Loc = glGetUniformLocation(mProgram1, "tex");
300         GLint program2Tex1Loc = glGetUniformLocation(mProgram2, "tex1");
301         GLint program2Tex2Loc = glGetUniformLocation(mProgram2, "tex2");
302 
303         glUseProgram(mProgram1);
304         glUniform1i(program1Tex1Loc, 0);
305 
306         glUseProgram(mProgram2);
307         glUniform1i(program2Tex1Loc, 0);
308         glUniform1i(program2Tex2Loc, 1);
309     }
310 
311     if (params.stateChange == StateChange::ManyTextureDraw)
312     {
313         GLint program3TexLocs[kManyTexturesCount];
314 
315         for (size_t i = 0; i < mTextures.size(); ++i)
316         {
317             char stringBuffer[8];
318             snprintf(stringBuffer, sizeof(stringBuffer), "tex%zu", i);
319             program3TexLocs[i] = glGetUniformLocation(mProgram3, stringBuffer);
320         }
321 
322         glUseProgram(mProgram3);
323         for (size_t i = 0; i < mTextures.size(); ++i)
324         {
325             glUniform1i(program3TexLocs[i], i);
326         }
327 
328         for (size_t i = 0; i < mTextures.size(); ++i)
329         {
330             glActiveTexture(GL_TEXTURE0 + i);
331             glBindTexture(GL_TEXTURE_2D, mTextures[i]);
332         }
333     }
334 
335     ASSERT_GL_NO_ERROR();
336 }
337 
destroyBenchmark()338 void DrawCallPerfBenchmark::destroyBenchmark()
339 {
340     glDeleteProgram(mProgram1);
341     glDeleteProgram(mProgram2);
342     glDeleteProgram(mProgram3);
343     glDeleteBuffers(1, &mBuffer1);
344     glDeleteBuffers(1, &mBuffer2);
345     glDeleteTextures(1, &mFBOTexture);
346     glDeleteTextures(mTextures.size(), mTextures.data());
347     glDeleteFramebuffers(1, &mFBO);
348 
349     if (!mVBOPool.empty())
350     {
351         glDeleteBuffers(mVBOPool.size(), mVBOPool.data());
352     }
353 }
354 
ClearThenDraw(unsigned int iterations,GLsizei numElements)355 void ClearThenDraw(unsigned int iterations, GLsizei numElements)
356 {
357     glClear(GL_COLOR_BUFFER_BIT);
358 
359     for (unsigned int it = 0; it < iterations; it++)
360     {
361         glDrawArrays(GL_TRIANGLES, 0, numElements);
362     }
363 }
364 
JustDraw(unsigned int iterations,GLsizei numElements)365 void JustDraw(unsigned int iterations, GLsizei numElements)
366 {
367     for (unsigned int it = 0; it < iterations; it++)
368     {
369         glDrawArrays(GL_TRIANGLES, 0, numElements);
370     }
371 }
372 
373 template <int kArrayBufferCount>
ChangeVertexAttribThenDraw(unsigned int iterations,GLsizei numElements,GLuint buffer)374 void ChangeVertexAttribThenDraw(unsigned int iterations, GLsizei numElements, GLuint buffer)
375 {
376     glBindBuffer(GL_ARRAY_BUFFER, buffer);
377     for (unsigned int it = 0; it < iterations; it++)
378     {
379         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
380         {
381             glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
382         }
383         glDrawArrays(GL_TRIANGLES, 0, numElements);
384 
385         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
386         {
387             glVertexAttribPointer(arrayIndex, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
388         }
389         glDrawArrays(GL_TRIANGLES, 0, numElements);
390     }
391 }
392 template <int kArrayBufferCount>
ChangeArrayBuffersThenDraw(unsigned int iterations,GLsizei numElements,GLuint buffer1,GLuint buffer2)393 void ChangeArrayBuffersThenDraw(unsigned int iterations,
394                                 GLsizei numElements,
395                                 GLuint buffer1,
396                                 GLuint buffer2)
397 {
398     for (unsigned int it = 0; it < iterations; it++)
399     {
400         glBindBuffer(GL_ARRAY_BUFFER, buffer1);
401         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
402         {
403             glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
404         }
405         glDrawArrays(GL_TRIANGLES, 0, numElements);
406 
407         glBindBuffer(GL_ARRAY_BUFFER, buffer2);
408         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
409         {
410             glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
411         }
412         glDrawArrays(GL_TRIANGLES, 0, numElements);
413     }
414 }
415 
ChangeTextureThenDraw(unsigned int iterations,GLsizei numElements,GLuint texture1,GLuint texture2)416 void ChangeTextureThenDraw(unsigned int iterations,
417                            GLsizei numElements,
418                            GLuint texture1,
419                            GLuint texture2)
420 {
421     for (unsigned int it = 0; it < iterations; it++)
422     {
423         glBindTexture(GL_TEXTURE_2D, texture1);
424         glDrawArrays(GL_TRIANGLES, 0, numElements);
425 
426         glBindTexture(GL_TEXTURE_2D, texture2);
427         glDrawArrays(GL_TRIANGLES, 0, numElements);
428     }
429 }
430 
ChangeProgramThenDraw(unsigned int iterations,GLsizei numElements,GLuint program1,GLuint program2)431 void ChangeProgramThenDraw(unsigned int iterations,
432                            GLsizei numElements,
433                            GLuint program1,
434                            GLuint program2)
435 {
436     for (unsigned int it = 0; it < iterations; it++)
437     {
438         glUseProgram(program1);
439         glDrawArrays(GL_TRIANGLES, 0, numElements);
440 
441         glUseProgram(program2);
442         glDrawArrays(GL_TRIANGLES, 0, numElements);
443     }
444 }
445 
CycleVertexBufferThenDraw(unsigned int iterations,GLsizei numElements,const std::vector<GLuint> & vbos,size_t * currentVBO)446 void CycleVertexBufferThenDraw(unsigned int iterations,
447                                GLsizei numElements,
448                                const std::vector<GLuint> &vbos,
449                                size_t *currentVBO)
450 {
451     for (unsigned int it = 0; it < iterations; it++)
452     {
453         GLuint vbo = vbos[*currentVBO];
454         glBindBuffer(GL_ARRAY_BUFFER, vbo);
455         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
456         glDrawArrays(GL_TRIANGLES, 0, numElements);
457         *currentVBO = (*currentVBO + 1) % vbos.size();
458     }
459 }
460 
ChangeScissorThenDraw(unsigned int iterations,GLsizei numElements,unsigned int windowWidth,unsigned int windowHeight)461 void ChangeScissorThenDraw(unsigned int iterations,
462                            GLsizei numElements,
463                            unsigned int windowWidth,
464                            unsigned int windowHeight)
465 {
466     // Change scissor as such:
467     //
468     // - Start with a narrow vertical bar:
469     //
470     //           Scissor
471     //              |
472     //              V
473     //       +-----+-+-----+
474     //       |     | |     | <-- Window
475     //       |     | |     |
476     //       |     | |     |
477     //       |     | |     |
478     //       |     | |     |
479     //       |     | |     |
480     //       +-----+-+-----+
481     //
482     // - Gradually reduce height and increase width, to end up with a narrow horizontal bar:
483     //
484     //       +-------------+
485     //       |             |
486     //       |             |
487     //       +-------------+ <-- Scissor
488     //       +-------------+
489     //       |             |
490     //       |             |
491     //       +-------------+
492     //
493     // - If more iterations left, restart, but shift the initial bar left to cover more area:
494     //
495     //       +---+-+-------+          +-------------+
496     //       |   | |       |          |             |
497     //       |   | |       |          +-------------+
498     //       |   | |       |   --->   |             |
499     //       |   | |       |          |             |
500     //       |   | |       |          +-------------+
501     //       |   | |       |          |             |
502     //       +---+-+-------+          +-------------+
503     //
504     //       +-+-+---------+          +-------------+
505     //       | | |         |          +-------------+
506     //       | | |         |          |             |
507     //       | | |         |   --->   |             |
508     //       | | |         |          |             |
509     //       | | |         |          |             |
510     //       | | |         |          +-------------+
511     //       +-+-+---------+          +-------------+
512 
513     glEnable(GL_SCISSOR_TEST);
514 
515     constexpr unsigned int kScissorStep  = 2;
516     unsigned int scissorX                = windowWidth / 2 - 1;
517     unsigned int scissorY                = 0;
518     unsigned int scissorWidth            = 2;
519     unsigned int scissorHeight           = windowHeight;
520     unsigned int scissorPatternIteration = 0;
521 
522     for (unsigned int it = 0; it < iterations; it++)
523     {
524         glScissor(scissorX, scissorY, scissorWidth, scissorHeight);
525         glDrawArrays(GL_TRIANGLES, 0, numElements);
526 
527         if (scissorX < kScissorStep || scissorHeight < kScissorStep * 2)
528         {
529             ++scissorPatternIteration;
530             scissorX      = windowWidth / 2 - 1 - scissorPatternIteration * 2;
531             scissorY      = 0;
532             scissorWidth  = 2;
533             scissorHeight = windowHeight;
534         }
535         else
536         {
537             scissorX -= kScissorStep;
538             scissorY += kScissorStep;
539             scissorWidth += kScissorStep * 2;
540             scissorHeight -= kScissorStep * 2;
541         }
542     }
543 }
544 
DrawWithEightTextures(unsigned int iterations,GLsizei numElements,std::vector<GLuint> textures)545 void DrawWithEightTextures(unsigned int iterations,
546                            GLsizei numElements,
547                            std::vector<GLuint> textures)
548 {
549     for (unsigned int it = 0; it < iterations; it++)
550     {
551         for (size_t i = 0; i < textures.size(); ++i)
552         {
553             glActiveTexture(GL_TEXTURE0 + i);
554             size_t index = (it + i) % textures.size();
555             glBindTexture(GL_TEXTURE_2D, textures[index]);
556         }
557 
558         glDrawArrays(GL_TRIANGLES, 0, numElements);
559     }
560 }
561 
UpdateUniformThenDraw(unsigned int iterations,GLsizei numElements)562 void UpdateUniformThenDraw(unsigned int iterations, GLsizei numElements)
563 {
564     for (unsigned int it = 0; it < iterations; it++)
565     {
566         float f = static_cast<float>(it) / static_cast<float>(iterations);
567         glUniform4f(0, f, f + 0.1f, f + 0.2f, f + 0.3f);
568         glDrawArrays(GL_TRIANGLES, 0, numElements);
569     }
570 }
571 
drawBenchmark()572 void DrawCallPerfBenchmark::drawBenchmark()
573 {
574     // This workaround fixes a huge queue of graphics commands accumulating on the GL
575     // back-end. The GL back-end doesn't have a proper NULL device at the moment.
576     // TODO(jmadill): Remove this when/if we ever get a proper OpenGL NULL device.
577     const auto &eglParams = GetParam().eglParameters;
578     const auto &params    = GetParam();
579     GLsizei numElements   = static_cast<GLsizei>(3 * mNumTris);
580 
581     switch (params.stateChange)
582     {
583         case StateChange::VertexAttrib:
584             ChangeVertexAttribThenDraw<1>(params.iterationsPerStep, numElements, mBuffer1);
585             break;
586         case StateChange::VertexBuffer:
587             ChangeArrayBuffersThenDraw<1>(params.iterationsPerStep, numElements, mBuffer1,
588                                           mBuffer2);
589             break;
590         case StateChange::ManyVertexBuffers:
591             ChangeArrayBuffersThenDraw<5>(params.iterationsPerStep, numElements, mBuffer1,
592                                           mBuffer2);
593             break;
594         case StateChange::Texture:
595             ChangeTextureThenDraw(params.iterationsPerStep, numElements, mTextures[0],
596                                   mTextures[1]);
597             break;
598         case StateChange::Program:
599             ChangeProgramThenDraw(params.iterationsPerStep, numElements, mProgram1, mProgram2);
600             break;
601         case StateChange::NoChange:
602             if (eglParams.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE ||
603                 (eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE &&
604                  eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE))
605             {
606                 ClearThenDraw(params.iterationsPerStep, numElements);
607             }
608             else
609             {
610                 JustDraw(params.iterationsPerStep, numElements);
611             }
612             break;
613         case StateChange::VertexBufferCycle:
614             CycleVertexBufferThenDraw(params.iterationsPerStep, numElements, mVBOPool,
615                                       &mCurrentVBO);
616             break;
617         case StateChange::Scissor:
618             ChangeScissorThenDraw(params.iterationsPerStep, numElements, getWindow()->getWidth(),
619                                   getWindow()->getHeight());
620             break;
621         case StateChange::ManyTextureDraw:
622             glUseProgram(mProgram3);
623             DrawWithEightTextures(params.iterationsPerStep, numElements, mTextures);
624             break;
625         case StateChange::Uniform:
626             UpdateUniformThenDraw(params.iterationsPerStep, numElements);
627             break;
628         case StateChange::InvalidEnum:
629             ADD_FAILURE() << "Invalid state change.";
630             break;
631     }
632 
633     ASSERT_GL_NO_ERROR();
634 }
635 
TEST_P(DrawCallPerfBenchmark,Run)636 TEST_P(DrawCallPerfBenchmark, Run)
637 {
638     run();
639 }
640 
641 using namespace params;
642 
CombineStateChange(const DrawArraysPerfParams & in,StateChange stateChange)643 DrawArraysPerfParams CombineStateChange(const DrawArraysPerfParams &in, StateChange stateChange)
644 {
645     DrawArraysPerfParams out = in;
646     out.stateChange          = stateChange;
647 
648     // Crank up iteration count to ensure we cycle through all VBs before a swap.
649     if (stateChange == StateChange::VertexBufferCycle)
650     {
651         out.iterationsPerStep = kCycleVBOPoolSize * 2;
652     }
653 
654     return out;
655 }
656 
657 using P = DrawArraysPerfParams;
658 
659 std::vector<P> gTestsWithStateChange =
660     CombineWithValues({P()}, angle::AllEnums<StateChange>(), CombineStateChange);
661 std::vector<P> gTestsWithRenderer =
662     CombineWithFuncs(gTestsWithStateChange, {D3D11<P>, GL<P>, Metal<P>, Vulkan<P>, WGL<P>});
663 std::vector<P> gTestsWithDevice =
664     CombineWithFuncs(gTestsWithRenderer, {Passthrough<P>, Offscreen<P>, NullDevice<P>});
665 
666 ANGLE_INSTANTIATE_TEST_ARRAY(DrawCallPerfBenchmark, gTestsWithDevice);
667 
668 }  // anonymous namespace
669