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 ¶ms)
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 ¶ms = 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 ¶ms = 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