• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &params)
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 &params = 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 &params = 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 &params = 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 &params = 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