• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace
17 {
18 enum class StateChange
19 {
20     NoChange,
21     VertexAttrib,
22     VertexBuffer,
23     ManyVertexBuffers,
24     Texture,
25     Program,
26     InvalidEnum,
27 };
28 
29 struct DrawArraysPerfParams : public DrawCallPerfParams
30 {
31     DrawArraysPerfParams() = default;
DrawArraysPerfParams__anon3c306b210111::DrawArraysPerfParams32     DrawArraysPerfParams(const DrawCallPerfParams &base) : DrawCallPerfParams(base) {}
33 
34     std::string story() const override;
35 
36     StateChange stateChange = StateChange::NoChange;
37 };
38 
story() const39 std::string DrawArraysPerfParams::story() const
40 {
41     std::stringstream strstr;
42 
43     strstr << DrawCallPerfParams::story();
44 
45     switch (stateChange)
46     {
47         case StateChange::VertexAttrib:
48             strstr << "_attrib_change";
49             break;
50         case StateChange::VertexBuffer:
51             strstr << "_vbo_change";
52             break;
53         case StateChange::ManyVertexBuffers:
54             strstr << "_manyvbos_change";
55             break;
56         case StateChange::Texture:
57             strstr << "_tex_change";
58             break;
59         case StateChange::Program:
60             strstr << "_prog_change";
61             break;
62         default:
63             break;
64     }
65 
66     return strstr.str();
67 }
68 
operator <<(std::ostream & os,const DrawArraysPerfParams & params)69 std::ostream &operator<<(std::ostream &os, const DrawArraysPerfParams &params)
70 {
71     os << params.backendAndStory().substr(1);
72     return os;
73 }
74 
CreateSimpleTexture2D()75 GLuint CreateSimpleTexture2D()
76 {
77     // Use tightly packed data
78     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
79 
80     // Generate a texture object
81     GLuint texture;
82     glGenTextures(1, &texture);
83 
84     // Bind the texture object
85     glBindTexture(GL_TEXTURE_2D, texture);
86 
87     // Load the texture: 2x2 Image, 3 bytes per pixel (R, G, B)
88     constexpr size_t width             = 2;
89     constexpr size_t height            = 2;
90     GLubyte pixels[width * height * 3] = {
91         255, 0,   0,    // Red
92         0,   255, 0,    // Green
93         0,   0,   255,  // Blue
94         255, 255, 0,    // Yellow
95     };
96     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
97 
98     // Set the filtering mode
99     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
100     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
101 
102     return texture;
103 }
104 
105 class DrawCallPerfBenchmark : public ANGLERenderTest,
106                               public ::testing::WithParamInterface<DrawArraysPerfParams>
107 {
108   public:
109     DrawCallPerfBenchmark();
110 
111     void initializeBenchmark() override;
112     void destroyBenchmark() override;
113     void drawBenchmark() override;
114 
115   private:
116     GLuint mProgram1   = 0;
117     GLuint mProgram2   = 0;
118     GLuint mBuffer1    = 0;
119     GLuint mBuffer2    = 0;
120     GLuint mFBO        = 0;
121     GLuint mFBOTexture = 0;
122     GLuint mTexture1   = 0;
123     GLuint mTexture2   = 0;
124     int mNumTris       = GetParam().numTris;
125 };
126 
DrawCallPerfBenchmark()127 DrawCallPerfBenchmark::DrawCallPerfBenchmark() : ANGLERenderTest("DrawCallPerf", GetParam()) {}
128 
initializeBenchmark()129 void DrawCallPerfBenchmark::initializeBenchmark()
130 {
131     const auto &params = GetParam();
132 
133     if (params.stateChange == StateChange::Texture)
134     {
135         mProgram1 = SetupSimpleTextureProgram();
136     }
137     if (params.stateChange == StateChange::Program)
138     {
139         mProgram1 = SetupSimpleTextureProgram();
140         mProgram2 = SetupDoubleTextureProgram();
141 
142         ASSERT_NE(0u, mProgram2);
143     }
144     else if (params.stateChange == StateChange::ManyVertexBuffers)
145     {
146         constexpr char kVS[] = R"(attribute vec2 vPosition;
147 attribute vec2 v0;
148 attribute vec2 v1;
149 attribute vec2 v2;
150 attribute vec2 v3;
151 const float scale = 0.5;
152 const float offset = -0.5;
153 
154 varying vec2 v;
155 
156 void main()
157 {
158     gl_Position = vec4(vPosition * vec2(scale) + vec2(offset), 0, 1);
159     v = (v0 + v1 + v2 + v3) * 0.25;
160 })";
161 
162         constexpr char kFS[] = R"(precision mediump float;
163 varying vec2 v;
164 void main()
165 {
166     gl_FragColor = vec4(v, 0, 1);
167 })";
168 
169         mProgram1 = CompileProgram(kVS, kFS);
170         glBindAttribLocation(mProgram1, 1, "v0");
171         glBindAttribLocation(mProgram1, 2, "v1");
172         glBindAttribLocation(mProgram1, 3, "v2");
173         glBindAttribLocation(mProgram1, 4, "v3");
174         glEnableVertexAttribArray(1);
175         glEnableVertexAttribArray(2);
176         glEnableVertexAttribArray(3);
177         glEnableVertexAttribArray(4);
178     }
179     else
180     {
181         mProgram1 = SetupSimpleDrawProgram();
182     }
183 
184     ASSERT_NE(0u, mProgram1);
185 
186     // Re-link program to ensure the attrib bindings are used.
187     glBindAttribLocation(mProgram1, 0, "vPosition");
188     glLinkProgram(mProgram1);
189     glUseProgram(mProgram1);
190 
191     if (mProgram2)
192     {
193         glBindAttribLocation(mProgram2, 0, "vPosition");
194         glLinkProgram(mProgram2);
195     }
196 
197     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
198 
199     mBuffer1 = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
200     mBuffer2 = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
201 
202     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
203     glEnableVertexAttribArray(0);
204 
205     // Set the viewport
206     glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
207 
208     if (params.offscreen)
209     {
210         CreateColorFBO(getWindow()->getWidth(), getWindow()->getHeight(), &mFBOTexture, &mFBO);
211     }
212 
213     mTexture1 = CreateSimpleTexture2D();
214     mTexture2 = CreateSimpleTexture2D();
215 
216     if (params.stateChange == StateChange::Program)
217     {
218         // Bind the textures as appropriate, they are not modified during the test.
219         GLint program1Tex1Loc = glGetUniformLocation(mProgram1, "tex");
220         GLint program2Tex1Loc = glGetUniformLocation(mProgram2, "tex1");
221         GLint program2Tex2Loc = glGetUniformLocation(mProgram2, "tex2");
222 
223         glUseProgram(mProgram1);
224         glUniform1i(program1Tex1Loc, 0);
225 
226         glUseProgram(mProgram2);
227         glUniform1i(program2Tex1Loc, 0);
228         glUniform1i(program2Tex2Loc, 1);
229 
230         glActiveTexture(GL_TEXTURE0);
231         glBindTexture(GL_TEXTURE_2D, mTexture1);
232 
233         glActiveTexture(GL_TEXTURE1);
234         glBindTexture(GL_TEXTURE_2D, mTexture2);
235     }
236 
237     ASSERT_GL_NO_ERROR();
238 }
239 
destroyBenchmark()240 void DrawCallPerfBenchmark::destroyBenchmark()
241 {
242     glDeleteProgram(mProgram1);
243     glDeleteProgram(mProgram2);
244     glDeleteBuffers(1, &mBuffer1);
245     glDeleteBuffers(1, &mBuffer2);
246     glDeleteTextures(1, &mFBOTexture);
247     glDeleteTextures(1, &mTexture1);
248     glDeleteTextures(1, &mTexture2);
249     glDeleteFramebuffers(1, &mFBO);
250 }
251 
ClearThenDraw(unsigned int iterations,GLsizei numElements)252 void ClearThenDraw(unsigned int iterations, GLsizei numElements)
253 {
254     glClear(GL_COLOR_BUFFER_BIT);
255 
256     for (unsigned int it = 0; it < iterations; it++)
257     {
258         glDrawArrays(GL_TRIANGLES, 0, numElements);
259     }
260 }
261 
JustDraw(unsigned int iterations,GLsizei numElements)262 void JustDraw(unsigned int iterations, GLsizei numElements)
263 {
264     for (unsigned int it = 0; it < iterations; it++)
265     {
266         glDrawArrays(GL_TRIANGLES, 0, numElements);
267     }
268 }
269 
270 template <int kArrayBufferCount>
ChangeVertexAttribThenDraw(unsigned int iterations,GLsizei numElements,GLuint buffer)271 void ChangeVertexAttribThenDraw(unsigned int iterations, GLsizei numElements, GLuint buffer)
272 {
273     glBindBuffer(GL_ARRAY_BUFFER, buffer);
274     for (unsigned int it = 0; it < iterations; it++)
275     {
276         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
277         {
278             glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
279         }
280         glDrawArrays(GL_TRIANGLES, 0, numElements);
281 
282         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
283         {
284             glVertexAttribPointer(arrayIndex, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
285         }
286         glDrawArrays(GL_TRIANGLES, 0, numElements);
287     }
288 }
289 template <int kArrayBufferCount>
ChangeArrayBuffersThenDraw(unsigned int iterations,GLsizei numElements,GLuint buffer1,GLuint buffer2)290 void ChangeArrayBuffersThenDraw(unsigned int iterations,
291                                 GLsizei numElements,
292                                 GLuint buffer1,
293                                 GLuint buffer2)
294 {
295     for (unsigned int it = 0; it < iterations; it++)
296     {
297         glBindBuffer(GL_ARRAY_BUFFER, buffer1);
298         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
299         {
300             glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
301         }
302         glDrawArrays(GL_TRIANGLES, 0, numElements);
303 
304         glBindBuffer(GL_ARRAY_BUFFER, buffer2);
305         for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
306         {
307             glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
308         }
309         glDrawArrays(GL_TRIANGLES, 0, numElements);
310     }
311 }
312 
ChangeTextureThenDraw(unsigned int iterations,GLsizei numElements,GLuint texture1,GLuint texture2)313 void ChangeTextureThenDraw(unsigned int iterations,
314                            GLsizei numElements,
315                            GLuint texture1,
316                            GLuint texture2)
317 {
318     for (unsigned int it = 0; it < iterations; it++)
319     {
320         glBindTexture(GL_TEXTURE_2D, texture1);
321         glDrawArrays(GL_TRIANGLES, 0, numElements);
322 
323         glBindTexture(GL_TEXTURE_2D, texture2);
324         glDrawArrays(GL_TRIANGLES, 0, numElements);
325     }
326 }
327 
ChangeProgramThenDraw(unsigned int iterations,GLsizei numElements,GLuint program1,GLuint program2)328 void ChangeProgramThenDraw(unsigned int iterations,
329                            GLsizei numElements,
330                            GLuint program1,
331                            GLuint program2)
332 {
333     for (unsigned int it = 0; it < iterations; it++)
334     {
335         glUseProgram(program1);
336         glDrawArrays(GL_TRIANGLES, 0, numElements);
337 
338         glUseProgram(program2);
339         glDrawArrays(GL_TRIANGLES, 0, numElements);
340     }
341 }
342 
drawBenchmark()343 void DrawCallPerfBenchmark::drawBenchmark()
344 {
345     // This workaround fixes a huge queue of graphics commands accumulating on the GL
346     // back-end. The GL back-end doesn't have a proper NULL device at the moment.
347     // TODO(jmadill): Remove this when/if we ever get a proper OpenGL NULL device.
348     const auto &eglParams = GetParam().eglParameters;
349     const auto &params    = GetParam();
350     GLsizei numElements   = static_cast<GLsizei>(3 * mNumTris);
351 
352     switch (params.stateChange)
353     {
354         case StateChange::VertexAttrib:
355             ChangeVertexAttribThenDraw<1>(params.iterationsPerStep, numElements, mBuffer1);
356             break;
357         case StateChange::VertexBuffer:
358             ChangeArrayBuffersThenDraw<1>(params.iterationsPerStep, numElements, mBuffer1,
359                                           mBuffer2);
360             break;
361         case StateChange::ManyVertexBuffers:
362             ChangeArrayBuffersThenDraw<5>(params.iterationsPerStep, numElements, mBuffer1,
363                                           mBuffer2);
364             break;
365         case StateChange::Texture:
366             ChangeTextureThenDraw(params.iterationsPerStep, numElements, mTexture1, mTexture2);
367             break;
368         case StateChange::Program:
369             ChangeProgramThenDraw(params.iterationsPerStep, numElements, mProgram1, mProgram2);
370             break;
371         case StateChange::NoChange:
372             if (eglParams.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE ||
373                 (eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE &&
374                  eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE))
375             {
376                 ClearThenDraw(params.iterationsPerStep, numElements);
377             }
378             else
379             {
380                 JustDraw(params.iterationsPerStep, numElements);
381             }
382             break;
383         case StateChange::InvalidEnum:
384             FAIL() << "Invalid state change.";
385             break;
386     }
387 
388     ASSERT_GL_NO_ERROR();
389 }
390 
TEST_P(DrawCallPerfBenchmark,Run)391 TEST_P(DrawCallPerfBenchmark, Run)
392 {
393     run();
394 }
395 
396 using namespace params;
397 
CombineStateChange(const DrawArraysPerfParams & in,StateChange stateChange)398 DrawArraysPerfParams CombineStateChange(const DrawArraysPerfParams &in, StateChange stateChange)
399 {
400     DrawArraysPerfParams out = in;
401     out.stateChange          = stateChange;
402     return out;
403 }
404 
405 using P = DrawArraysPerfParams;
406 
407 std::vector<P> gTestsWithStateChange =
408     CombineWithValues({P()}, angle::AllEnums<StateChange>(), CombineStateChange);
409 std::vector<P> gTestsWithRenderer =
410     CombineWithFuncs(gTestsWithStateChange, {D3D11<P>, GL<P>, Vulkan<P>, WGL<P>});
411 std::vector<P> gTestsWithDevice =
412     CombineWithFuncs(gTestsWithRenderer, {Passthrough<P>, Offscreen<P>, NullDevice<P>});
413 
414 ANGLE_INSTANTIATE_TEST_ARRAY(DrawCallPerfBenchmark, gTestsWithDevice);
415 
416 }  // anonymous namespace
417