1 //
2 // Copyright 2016 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 // UniformsBenchmark:
7 // Performance test for setting uniform data.
8 //
9
10 #include "ANGLEPerfTest.h"
11
12 #include <array>
13 #include <iostream>
14 #include <random>
15 #include <sstream>
16
17 #include "common/debug.h"
18 #include "util/Matrix.h"
19 #include "util/shader_utils.h"
20
21 using namespace angle;
22
23 namespace
24 {
25 constexpr unsigned int kIterationsPerStep = 4;
26
27 // Controls when we call glUniform, if the data is the same as last frame.
28 enum DataMode
29 {
30 UPDATE,
31 REPEAT,
32 };
33
34 // TODO(jmadill): Use an ANGLE enum for this?
35 enum DataType
36 {
37 VEC4,
38 MAT3x3,
39 MAT3x4,
40 MAT4x4,
41 };
42
43 // Determines if we state change the program between draws.
44 // This covers a performance problem in ANGLE where calling UseProgram reuploads uniform data.
45 enum ProgramMode
46 {
47 SINGLE,
48 MULTIPLE,
49 };
50
51 enum MatrixLayout
52 {
53 TRANSPOSE,
54 NO_TRANSPOSE,
55 };
56
57 struct UniformsParams final : public RenderTestParams
58 {
UniformsParams__anon8bba21aa0111::UniformsParams59 UniformsParams()
60 {
61 iterationsPerStep = kIterationsPerStep;
62
63 // Common default params
64 majorVersion = 3;
65 minorVersion = 0;
66 windowWidth = 720;
67 windowHeight = 720;
68 }
69
70 std::string story() const override;
71 size_t numVertexUniforms = 200;
72 size_t numFragmentUniforms = 200;
73
74 DataType dataType = DataType::VEC4;
75 DataMode dataMode = DataMode::REPEAT;
76 MatrixLayout matrixLayout = MatrixLayout::NO_TRANSPOSE;
77 ProgramMode programMode = ProgramMode::SINGLE;
78 };
79
operator <<(std::ostream & os,const UniformsParams & params)80 std::ostream &operator<<(std::ostream &os, const UniformsParams ¶ms)
81 {
82 os << params.backendAndStory().substr(1);
83 return os;
84 }
85
story() const86 std::string UniformsParams::story() const
87 {
88 std::stringstream strstr;
89
90 strstr << RenderTestParams::story();
91
92 if (eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
93 {
94 strstr << "_null";
95 }
96
97 if (dataType == DataType::VEC4)
98 {
99 strstr << "_" << (numVertexUniforms + numFragmentUniforms) << "_vec4";
100 }
101 else if (dataType == DataType::MAT3x3)
102 {
103 strstr << "_" << (numVertexUniforms + numFragmentUniforms) << "_mat3x3";
104 }
105 else if (dataType == DataType::MAT3x4)
106 {
107 strstr << "_" << (numVertexUniforms + numFragmentUniforms) << "_mat3x4";
108 }
109 else
110 {
111 strstr << "_" << (numVertexUniforms + numFragmentUniforms) << "_mat4x4";
112 }
113
114 if (matrixLayout == MatrixLayout::TRANSPOSE)
115 {
116 strstr << "_transpose";
117 }
118
119 if (programMode == ProgramMode::MULTIPLE)
120 {
121 strstr << "_multiprogram";
122 }
123
124 if (dataMode == DataMode::REPEAT)
125 {
126 strstr << "_repeating";
127 }
128
129 return strstr.str();
130 }
131
132 class UniformsBenchmark : public ANGLERenderTest,
133 public ::testing::WithParamInterface<UniformsParams>
134 {
135 public:
136 UniformsBenchmark();
137
138 void initializeBenchmark() override;
139 void destroyBenchmark() override;
140 void drawBenchmark() override;
141
142 private:
143 void initShaders();
144
145 template <bool MultiProgram, typename SetUniformFunc>
146 void drawLoop(const SetUniformFunc &setUniformsFunc);
147
148 std::array<GLuint, 2> mPrograms;
149 std::vector<GLuint> mUniformLocations;
150
151 using MatrixData = std::array<std::vector<Matrix4>, 2>;
152 MatrixData mMatrixData;
153 };
154
GenMatrixData(size_t count,int parity)155 std::vector<Matrix4> GenMatrixData(size_t count, int parity)
156 {
157 std::vector<Matrix4> data;
158
159 // Very simple matrix data allocation scheme.
160 for (size_t index = 0; index < count; ++index)
161 {
162 Matrix4 mat;
163 for (int row = 0; row < 4; ++row)
164 {
165 for (int col = 0; col < 4; ++col)
166 {
167 mat.data[row * 4 + col] = (row * col + parity) % 2 == 0 ? 1.0f : -1.0f;
168 }
169 }
170
171 data.push_back(mat);
172 }
173
174 return data;
175 }
176
UniformsBenchmark()177 UniformsBenchmark::UniformsBenchmark() : ANGLERenderTest("Uniforms", GetParam()), mPrograms({})
178 {
179 // Fails on Windows NVIDIA Vulkan. http://crbug.com/1041672
180 if (IsWindows() && IsNVIDIA() &&
181 GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
182 {
183 mSkipTest = true;
184 }
185 }
186
initializeBenchmark()187 void UniformsBenchmark::initializeBenchmark()
188 {
189 const auto ¶ms = GetParam();
190
191 // Verify the uniform counts are within the limits
192 GLint maxVertexUniformVectors, maxFragmentUniformVectors;
193 glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniformVectors);
194 glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniformVectors);
195
196 GLint vectorCountPerUniform;
197 bool isMatrix;
198 switch (params.dataType)
199 {
200 case DataType::MAT3x3:
201 vectorCountPerUniform = 3;
202 isMatrix = true;
203 break;
204 case DataType::MAT3x4:
205 // depends on transpose, conservatively set to 4
206 vectorCountPerUniform = 4;
207 isMatrix = true;
208 break;
209 case DataType::MAT4x4:
210 vectorCountPerUniform = 4;
211 isMatrix = true;
212 break;
213 default:
214 vectorCountPerUniform = 1;
215 isMatrix = false;
216 break;
217 }
218
219 GLint numVertexUniformVectors =
220 static_cast<GLint>(params.numVertexUniforms) * vectorCountPerUniform;
221 GLint numFragmentUniformVectors =
222 static_cast<GLint>(params.numFragmentUniforms) * vectorCountPerUniform;
223
224 if (numVertexUniformVectors > maxVertexUniformVectors)
225 {
226 FAIL() << "Vertex uniform vector count (" << numVertexUniformVectors << ")"
227 << " exceeds maximum vertex uniform vector count: " << maxVertexUniformVectors
228 << std::endl;
229 }
230 if (numFragmentUniformVectors > maxFragmentUniformVectors)
231 {
232 FAIL() << "Fragment uniform vector count (" << numFragmentUniformVectors << ")"
233 << " exceeds maximum fragment uniform vector count: " << maxFragmentUniformVectors
234 << std::endl;
235 }
236
237 initShaders();
238 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
239 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
240
241 if (isMatrix)
242 {
243 size_t count = params.numVertexUniforms + params.numFragmentUniforms;
244
245 mMatrixData[0] = GenMatrixData(count, 0);
246 if (params.dataMode == DataMode::REPEAT)
247 {
248 mMatrixData[1] = GenMatrixData(count, 0);
249 }
250 else
251 {
252 mMatrixData[1] = GenMatrixData(count, 1);
253 }
254 }
255
256 GLint attribLocation = glGetAttribLocation(mPrograms[0], "pos");
257 ASSERT_NE(-1, attribLocation);
258 ASSERT_EQ(attribLocation, glGetAttribLocation(mPrograms[1], "pos"));
259 glVertexAttrib4f(attribLocation, 1.0f, 0.0f, 0.0f, 1.0f);
260
261 ASSERT_GL_NO_ERROR();
262 }
263
GetUniformLocationName(size_t idx,bool vertexShader)264 std::string GetUniformLocationName(size_t idx, bool vertexShader)
265 {
266 std::stringstream strstr;
267 strstr << (vertexShader ? "vs" : "fs") << "_u_" << idx;
268 return strstr.str();
269 }
270
initShaders()271 void UniformsBenchmark::initShaders()
272 {
273 const auto ¶ms = GetParam();
274
275 const std::string kUniformVarPlaceHolder = "%s";
276 std::string typeString;
277 std::string uniformOperationTemplate;
278 switch (params.dataType)
279 {
280 case DataType::VEC4:
281 typeString = "vec4";
282 uniformOperationTemplate = kUniformVarPlaceHolder;
283 break;
284 case DataType::MAT3x3:
285 typeString = "mat3";
286 uniformOperationTemplate =
287 "mat4(" + kUniformVarPlaceHolder + ") * vec4(1.0, 1.0, 1.0, 1.0)";
288 break;
289 case DataType::MAT3x4:
290 typeString = "mat3x4";
291 uniformOperationTemplate =
292 "mat4(" + kUniformVarPlaceHolder + ") * vec4(1.0, 1.0, 1.0, 1.0)";
293 break;
294 case DataType::MAT4x4:
295 typeString = "mat4";
296 uniformOperationTemplate = kUniformVarPlaceHolder + "* vec4(1.0, 1.0, 1.0, 1.0)";
297 break;
298 default:
299 UNREACHABLE();
300 }
301
302 std::stringstream vstrstr;
303 vstrstr << "#version 300 es\n";
304 vstrstr << "precision mediump float;\n";
305 vstrstr << "in vec4 pos;\n";
306
307 for (size_t i = 0; i < params.numVertexUniforms; i++)
308 {
309 vstrstr << "uniform " << typeString << " " << GetUniformLocationName(i, true) << ";\n";
310 }
311
312 vstrstr << "void main()\n"
313 "{\n"
314 " gl_Position = pos;\n";
315 for (size_t i = 0; i < params.numVertexUniforms; i++)
316 {
317 std::string uniformOperation = uniformOperationTemplate;
318 std::size_t pos = uniformOperation.find(kUniformVarPlaceHolder);
319 ASSERT(pos != std::string::npos);
320 uniformOperation.replace(pos, kUniformVarPlaceHolder.size(),
321 GetUniformLocationName(i, true));
322 vstrstr << " gl_Position += ";
323 vstrstr << uniformOperation;
324 vstrstr << ";\n";
325 }
326 vstrstr << "}";
327
328 std::stringstream fstrstr;
329 fstrstr << "#version 300 es\n";
330 fstrstr << "precision mediump float;\n";
331 fstrstr << "out vec4 fragColor;\n";
332
333 for (size_t i = 0; i < params.numFragmentUniforms; i++)
334 {
335 fstrstr << "uniform " << typeString << " " << GetUniformLocationName(i, false) << ";\n";
336 }
337 fstrstr << "void main()\n"
338 "{\n"
339 " fragColor = vec4(0, 0, 0, 0);\n";
340 for (size_t i = 0; i < params.numFragmentUniforms; i++)
341 {
342 std::string uniformOperation = uniformOperationTemplate;
343 std::size_t pos = uniformOperation.find(kUniformVarPlaceHolder);
344 ASSERT(pos != std::string::npos);
345 uniformOperation.replace(pos, kUniformVarPlaceHolder.size(),
346 GetUniformLocationName(i, false));
347 fstrstr << " fragColor += ";
348 fstrstr << uniformOperation;
349 fstrstr << ";\n";
350 }
351 fstrstr << "}";
352
353 mPrograms[0] = CompileProgram(vstrstr.str().c_str(), fstrstr.str().c_str());
354 ASSERT_NE(0u, mPrograms[0]);
355 mPrograms[1] = CompileProgram(vstrstr.str().c_str(), fstrstr.str().c_str());
356 ASSERT_NE(0u, mPrograms[1]);
357
358 for (size_t i = 0; i < params.numVertexUniforms; ++i)
359 {
360 std::string name = GetUniformLocationName(i, true);
361 GLint location = glGetUniformLocation(mPrograms[0], name.c_str());
362 ASSERT_NE(-1, location);
363 ASSERT_EQ(location, glGetUniformLocation(mPrograms[1], name.c_str()));
364 mUniformLocations.push_back(location);
365 }
366 for (size_t i = 0; i < params.numFragmentUniforms; ++i)
367 {
368 std::string name = GetUniformLocationName(i, false);
369 GLint location = glGetUniformLocation(mPrograms[0], name.c_str());
370 ASSERT_NE(-1, location);
371 ASSERT_EQ(location, glGetUniformLocation(mPrograms[1], name.c_str()));
372 mUniformLocations.push_back(location);
373 }
374
375 // Use the program object
376 glUseProgram(mPrograms[0]);
377 }
378
destroyBenchmark()379 void UniformsBenchmark::destroyBenchmark()
380 {
381 glDeleteProgram(mPrograms[0]);
382 glDeleteProgram(mPrograms[1]);
383 }
384
385 // Hopefully the compiler is smart enough to inline the lambda setUniformsFunc.
386 template <bool MultiProgram, typename SetUniformFunc>
drawLoop(const SetUniformFunc & setUniformsFunc)387 void UniformsBenchmark::drawLoop(const SetUniformFunc &setUniformsFunc)
388 {
389 const auto ¶ms = GetParam();
390
391 size_t frameIndex = 0;
392
393 for (size_t it = 0; it < params.iterationsPerStep; ++it, frameIndex = (frameIndex == 0 ? 1 : 0))
394 {
395 if (MultiProgram)
396 {
397 glUseProgram(mPrograms[frameIndex]);
398 }
399 if (params.dataMode == DataMode::UPDATE)
400 {
401 for (size_t uniform = 0; uniform < mUniformLocations.size(); ++uniform)
402 {
403 setUniformsFunc(mUniformLocations, mMatrixData, uniform, frameIndex);
404 }
405 }
406 glDrawArrays(GL_TRIANGLES, 0, 3);
407 }
408 }
409
drawBenchmark()410 void UniformsBenchmark::drawBenchmark()
411 {
412 const auto ¶ms = GetParam();
413
414 GLboolean transpose = static_cast<GLboolean>(params.matrixLayout == MatrixLayout::TRANSPOSE);
415
416 switch (params.dataType)
417 {
418 case DataType::MAT4x4:
419 {
420 auto setFunc = [=](const std::vector<GLuint> &locations, const MatrixData &matrixData,
421 size_t uniform, size_t frameIndex) {
422 glUniformMatrix4fv(locations[uniform], 1, transpose,
423 matrixData[frameIndex][uniform].data);
424 };
425
426 drawLoop<false>(setFunc);
427 break;
428 }
429 case DataType::MAT3x4:
430 {
431 auto setFunc = [=](const std::vector<GLuint> &locations, const MatrixData &matrixData,
432 size_t uniform, size_t frameIndex) {
433 glUniformMatrix3x4fv(locations[uniform], 1, transpose,
434 matrixData[frameIndex][uniform].data);
435 };
436
437 drawLoop<false>(setFunc);
438 break;
439 }
440 case DataType::MAT3x3:
441 {
442 auto setFunc = [=](const std::vector<GLuint> &locations, const MatrixData &matrixData,
443 size_t uniform, size_t frameIndex) {
444 glUniformMatrix3fv(locations[uniform], 1, transpose,
445 matrixData[frameIndex][uniform].data);
446 };
447
448 drawLoop<false>(setFunc);
449 break;
450 }
451 case DataType::VEC4:
452 {
453 auto setFunc = [](const std::vector<GLuint> &locations, const MatrixData &matrixData,
454 size_t uniform, size_t frameIndex) {
455 float value = static_cast<float>(uniform);
456 glUniform4f(locations[uniform], value, value, value, value);
457 };
458
459 if (params.programMode == ProgramMode::MULTIPLE)
460 {
461 drawLoop<true>(setFunc);
462 }
463 else
464 {
465 drawLoop<false>(setFunc);
466 }
467 break;
468 }
469 default:
470 UNREACHABLE();
471 }
472
473 ASSERT_GL_NO_ERROR();
474 }
475
476 using namespace egl_platform;
477
VectorUniforms(const EGLPlatformParameters & egl,DataMode dataMode,ProgramMode programMode=ProgramMode::SINGLE)478 UniformsParams VectorUniforms(const EGLPlatformParameters &egl,
479 DataMode dataMode,
480 ProgramMode programMode = ProgramMode::SINGLE)
481 {
482 UniformsParams params;
483 params.eglParameters = egl;
484 params.dataMode = dataMode;
485 params.programMode = programMode;
486 return params;
487 }
488
MatrixUniforms(const EGLPlatformParameters & egl,DataMode dataMode,DataType dataType,MatrixLayout matrixLayout)489 UniformsParams MatrixUniforms(const EGLPlatformParameters &egl,
490 DataMode dataMode,
491 DataType dataType,
492 MatrixLayout matrixLayout)
493 {
494 UniformsParams params;
495 params.eglParameters = egl;
496 params.dataType = dataType;
497 params.dataMode = dataMode;
498 params.matrixLayout = matrixLayout;
499
500 // Reduce the number of uniforms to fit within smaller upper limits on some configs.
501 params.numVertexUniforms = 55;
502 params.numFragmentUniforms = 55;
503
504 return params;
505 }
506
507 } // anonymous namespace
508
TEST_P(UniformsBenchmark,Run)509 TEST_P(UniformsBenchmark, Run)
510 {
511 run();
512 }
513
514 ANGLE_INSTANTIATE_TEST(
515 UniformsBenchmark,
516 VectorUniforms(D3D11(), DataMode::REPEAT),
517 VectorUniforms(D3D11(), DataMode::UPDATE),
518 VectorUniforms(D3D11_NULL(), DataMode::UPDATE),
519 VectorUniforms(OPENGL_OR_GLES(), DataMode::UPDATE),
520 VectorUniforms(OPENGL_OR_GLES(), DataMode::REPEAT),
521 VectorUniforms(OPENGL_OR_GLES_NULL(), DataMode::UPDATE),
522 MatrixUniforms(D3D11(), DataMode::UPDATE, DataType::MAT4x4, MatrixLayout::NO_TRANSPOSE),
523 MatrixUniforms(OPENGL_OR_GLES(),
524 DataMode::UPDATE,
525 DataType::MAT4x4,
526 MatrixLayout::NO_TRANSPOSE),
527 MatrixUniforms(VULKAN_NULL(), DataMode::UPDATE, DataType::MAT4x4, MatrixLayout::NO_TRANSPOSE),
528 MatrixUniforms(VULKAN_NULL(), DataMode::UPDATE, DataType::MAT4x4, MatrixLayout::TRANSPOSE),
529 MatrixUniforms(VULKAN_NULL(), DataMode::REPEAT, DataType::MAT4x4, MatrixLayout::NO_TRANSPOSE),
530 MatrixUniforms(VULKAN_NULL(), DataMode::UPDATE, DataType::MAT3x4, MatrixLayout::NO_TRANSPOSE),
531 MatrixUniforms(VULKAN_NULL(), DataMode::UPDATE, DataType::MAT3x3, MatrixLayout::TRANSPOSE),
532 MatrixUniforms(VULKAN_NULL(), DataMode::REPEAT, DataType::MAT3x3, MatrixLayout::TRANSPOSE),
533 MatrixUniforms(VULKAN(), DataMode::UPDATE, DataType::MAT4x4, MatrixLayout::NO_TRANSPOSE),
534 MatrixUniforms(VULKAN(), DataMode::REPEAT, DataType::MAT4x4, MatrixLayout::NO_TRANSPOSE),
535 MatrixUniforms(VULKAN(), DataMode::UPDATE, DataType::MAT3x3, MatrixLayout::NO_TRANSPOSE),
536 MatrixUniforms(VULKAN(), DataMode::REPEAT, DataType::MAT3x3, MatrixLayout::NO_TRANSPOSE),
537 VectorUniforms(D3D11_NULL(), DataMode::REPEAT, ProgramMode::MULTIPLE));
538