xref: /aosp_15_r20/external/angle/src/tests/perf_tests/UniformsPerf.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 &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 (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 &params = 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 &params = 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 &params = 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 &params = 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