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