xref: /aosp_15_r20/external/angle/src/tests/perf_tests/ParallelLinkProgramPerfTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2023 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 // ParallelLinkProgramPerfTest:
7 //   Tests performance of compiling and linking many shaders and programs in sequence.
8 //
9 
10 #include "ANGLEPerfTest.h"
11 
12 #include <array>
13 
14 #include "common/vector_utils.h"
15 #include "util/shader_utils.h"
16 
17 using namespace angle;
18 
19 namespace
20 {
21 
22 enum class CompileLinkOrder
23 {
24     // Most optimal behavior; all compiles are done first, then all links.
25     AllCompilesFirst,
26     // Less optimal, but still decent; each program's shaders are first compiled, then the program
27     // is linked before moving on to the next program.
28     Interleaved,
29     // Worst behavior, which unfortunately is what the large majority of applications do; each
30     // program's shaders are compiled, the program is linked, and the link status is immediately
31     // queried (causing the main thread to block on the link task).
32     InterleavedAndImmediateQuery,
33 
34     Unspecified,
35 };
36 
37 struct ParallelLinkProgramParams final : public RenderTestParams
38 {
ParallelLinkProgramParams__anon97123a550111::ParallelLinkProgramParams39     ParallelLinkProgramParams(CompileLinkOrder order)
40     {
41         iterationsPerStep = 100;
42 
43         majorVersion     = 3;
44         minorVersion     = 0;
45         windowWidth      = 256;
46         windowHeight     = 256;
47         compileLinkOrder = order;
48     }
49 
story__anon97123a550111::ParallelLinkProgramParams50     std::string story() const override
51     {
52         std::stringstream strstr;
53         strstr << RenderTestParams::story();
54 
55         if (compileLinkOrder == CompileLinkOrder::AllCompilesFirst)
56         {
57             strstr << "_all_compiles_first";
58         }
59         else if (compileLinkOrder == CompileLinkOrder::Interleaved)
60         {
61             strstr << "_interleaved_compile_and_link";
62         }
63         else if (compileLinkOrder == CompileLinkOrder::InterleavedAndImmediateQuery)
64         {
65             strstr << "_interleaved_compile_and_link_with_immediate_query";
66         }
67 
68         if (std::find(eglParameters.disabledFeatureOverrides.begin(),
69                       eglParameters.disabledFeatureOverrides.end(),
70                       Feature::EnableParallelCompileAndLink) !=
71             eglParameters.disabledFeatureOverrides.end())
72         {
73             strstr << "_serial";
74         }
75 
76         if (eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
77         {
78             strstr << "_null";
79         }
80 
81         return strstr.str();
82     }
83 
84     CompileLinkOrder compileLinkOrder;
85 };
86 
operator <<(std::ostream & os,const ParallelLinkProgramParams & params)87 std::ostream &operator<<(std::ostream &os, const ParallelLinkProgramParams &params)
88 {
89     os << params.backendAndStory().substr(1);
90     return os;
91 }
92 
93 class ParallelLinkProgramBenchmark : public ANGLERenderTest,
94                                      public ::testing::WithParamInterface<ParallelLinkProgramParams>
95 {
96   public:
97     ParallelLinkProgramBenchmark();
98 
99     void initializeBenchmark() override;
100     void destroyBenchmark() override;
101     void drawBenchmark() override;
102 
103   protected:
104     struct Program
105     {
106         std::string vertexShader;
107         std::string fragmentShader;
108 
109         GLuint vs;
110         GLuint fs;
111         GLuint program;
112     };
113 
114     std::vector<Program> mPrograms;
115 };
116 
ParallelLinkProgramBenchmark()117 ParallelLinkProgramBenchmark::ParallelLinkProgramBenchmark()
118     : ANGLERenderTest("ParallelLinkProgram", GetParam())
119 {
120     if (IsWindows() && IsNVIDIA() &&
121         GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
122     {
123         skipTest("http://anglebug.com/42266835 crashes the GL driver");
124     }
125 
126     if (IsLinux() && IsNVIDIA() &&
127         GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
128     {
129         skipTest("http://anglebug.com/42266835 flakily crashes the GL driver");
130     }
131 }
132 
initializeBenchmark()133 void ParallelLinkProgramBenchmark::initializeBenchmark()
134 {
135     const ParallelLinkProgramParams &params = GetParam();
136 
137     // Generate N shaders and create the GL objects.  DrawBenchmark would then _only_ do compilation
138     // and link.
139     mPrograms.resize(params.iterationsPerStep);
140     for (uint32_t i = 0; i < params.iterationsPerStep; ++i)
141     {
142         std::ostringstream vs;
143         vs << R"(#version 300 es
144 uniform UBO
145 {
146     vec4 data[)"
147            << params.iterationsPerStep << R"(];
148 };
149 
150 uniform UBO2
151 {
152     mat4 m[2];
153 } ubo;
154 
155 in mediump vec4 attr1;
156 in mediump uvec4 attr2;
157 
158 out highp vec3 var1;
159 flat out lowp uvec2 var2;
160 
161 void main()
162 {
163     vec4 a = attr1 + vec4(attr2);)";
164         for (uint32_t j = 0; j < i * 5; ++j)
165         {
166             vs << R"(
167     a *= ubo.m[0];)";
168         }
169         vs << R"(
170     a += ubo.m[1][2];
171     var1 = cross(a.zxy, data[)"
172            << i << R"(].xxw);
173     var2 = uvec2(1, 3) * uvec2(6, 3);
174 })";
175         mPrograms[i].vertexShader = vs.str();
176 
177         std::ostringstream fs;
178         fs << R"(#version 300 es
179 uniform UBO
180 {
181     highp vec4 data[)"
182            << params.iterationsPerStep << R"(];
183 };
184 
185 in highp vec3 var1;
186 flat in lowp uvec2 var2;
187 
188 uniform mediump sampler2D s;
189 uniform mediump sampler2D s2;
190 
191 layout(location = 0) out mediump vec4 color1;
192 layout(location = 2) out highp uvec2 color2;
193 
194 void main()
195 {
196     color1 = var1.xyzx + texture(s, vec2(var2) / 9.);
197     mediump vec4 sum = vec4(0);
198     for (int i = 0; i < 10; ++i)
199       sum += texture(s2, vec2(float(i / 2) / 5., float(i % 5) / 5.));
200     uvec2 res = uvec2(sum.xz + sum.yw);)";
201         for (uint32_t j = 0; j < i * 5; ++j)
202         {
203             fs << R"(
204     res += uvec2(data[)"
205                << (j % 100) << R"(]);)";
206         }
207         fs << R"(
208     color2 = res;
209 })";
210         mPrograms[i].fragmentShader = fs.str();
211 
212         mPrograms[i].vs      = glCreateShader(GL_VERTEX_SHADER);
213         mPrograms[i].fs      = glCreateShader(GL_FRAGMENT_SHADER);
214         mPrograms[i].program = glCreateProgram();
215 
216         const char *vsCStr = mPrograms[i].vertexShader.c_str();
217         const char *fsCStr = mPrograms[i].fragmentShader.c_str();
218         glShaderSource(mPrograms[i].vs, 1, &vsCStr, 0);
219         glShaderSource(mPrograms[i].fs, 1, &fsCStr, 0);
220 
221         glAttachShader(mPrograms[i].program, mPrograms[i].vs);
222         glAttachShader(mPrograms[i].program, mPrograms[i].fs);
223     }
224 
225     ASSERT_GL_NO_ERROR();
226 }
227 
destroyBenchmark()228 void ParallelLinkProgramBenchmark::destroyBenchmark()
229 {
230     const ParallelLinkProgramParams &params = GetParam();
231 
232     for (uint32_t i = 0; i < params.iterationsPerStep; ++i)
233     {
234         glDetachShader(mPrograms[i].program, mPrograms[i].vs);
235         glDetachShader(mPrograms[i].program, mPrograms[i].fs);
236         glDeleteShader(mPrograms[i].vs);
237         glDeleteShader(mPrograms[i].fs);
238         glDeleteProgram(mPrograms[i].program);
239     }
240 }
241 
drawBenchmark()242 void ParallelLinkProgramBenchmark::drawBenchmark()
243 {
244     const ParallelLinkProgramParams &params = GetParam();
245 
246     for (uint32_t i = 0; i < params.iterationsPerStep; ++i)
247     {
248         // Compile the shaders, and if interleaved, link the corresponding programs.
249         glCompileShader(mPrograms[i].vs);
250         glCompileShader(mPrograms[i].fs);
251         if (params.compileLinkOrder != CompileLinkOrder::AllCompilesFirst)
252         {
253             glLinkProgram(mPrograms[i].program);
254 
255             if (params.compileLinkOrder == CompileLinkOrder::InterleavedAndImmediateQuery)
256             {
257                 GLint linkStatus = GL_TRUE;
258                 glGetProgramiv(mPrograms[i].program, GL_LINK_STATUS, &linkStatus);
259                 EXPECT_TRUE(linkStatus) << i;
260             }
261         }
262     }
263 
264     // If asked to link after all shaders are compiled, link all the programs now
265     if (params.compileLinkOrder == CompileLinkOrder::AllCompilesFirst)
266     {
267         for (uint32_t i = 0; i < params.iterationsPerStep; ++i)
268         {
269             glLinkProgram(mPrograms[i].program);
270         }
271     }
272 
273     // Now that all the compile and link jobs have been scheduled, wait for them all to finish.
274     for (uint32_t i = 0; i < params.iterationsPerStep; ++i)
275     {
276         GLint compileResult;
277         glGetShaderiv(mPrograms[i].vs, GL_COMPILE_STATUS, &compileResult);
278         EXPECT_NE(compileResult, 0) << i;
279         glGetShaderiv(mPrograms[i].fs, GL_COMPILE_STATUS, &compileResult);
280         EXPECT_NE(compileResult, 0) << i;
281 
282         GLint linkStatus = GL_TRUE;
283         glGetProgramiv(mPrograms[i].program, GL_LINK_STATUS, &linkStatus);
284         EXPECT_TRUE(linkStatus) << i;
285     }
286 
287     // ANGLE supports running some optional link subtasks beyond the actual end of the link.  Ensure
288     // those are all finished by triggerring a wait on the jobs of the last program.  Currently,
289     // detaching and attaching shaders does that (among other operations).
290     const uint32_t last = params.iterationsPerStep - 1;
291     glDetachShader(mPrograms[last].program, mPrograms[last].vs);
292     glAttachShader(mPrograms[last].program, mPrograms[last].vs);
293 
294     ASSERT_GL_NO_ERROR();
295 }
296 
297 using namespace egl_platform;
298 
ParallelLinkProgramD3D11Params(CompileLinkOrder compileLinkOrder)299 ParallelLinkProgramParams ParallelLinkProgramD3D11Params(CompileLinkOrder compileLinkOrder)
300 {
301     ParallelLinkProgramParams params(compileLinkOrder);
302     params.eglParameters = D3D11();
303     return params;
304 }
305 
ParallelLinkProgramMetalParams(CompileLinkOrder compileLinkOrder)306 ParallelLinkProgramParams ParallelLinkProgramMetalParams(CompileLinkOrder compileLinkOrder)
307 {
308     ParallelLinkProgramParams params(compileLinkOrder);
309     params.eglParameters = METAL();
310     return params;
311 }
312 
ParallelLinkProgramOpenGLOrGLESParams(CompileLinkOrder compileLinkOrder)313 ParallelLinkProgramParams ParallelLinkProgramOpenGLOrGLESParams(CompileLinkOrder compileLinkOrder)
314 {
315     ParallelLinkProgramParams params(compileLinkOrder);
316     params.eglParameters = OPENGL_OR_GLES();
317     return params;
318 }
319 
ParallelLinkProgramVulkanParams(CompileLinkOrder compileLinkOrder)320 ParallelLinkProgramParams ParallelLinkProgramVulkanParams(CompileLinkOrder compileLinkOrder)
321 {
322     ParallelLinkProgramParams params(compileLinkOrder);
323     params.eglParameters = VULKAN();
324     params.enable(Feature::EnableParallelCompileAndLink);
325     return params;
326 }
327 
SerialLinkProgramVulkanParams(CompileLinkOrder compileLinkOrder)328 ParallelLinkProgramParams SerialLinkProgramVulkanParams(CompileLinkOrder compileLinkOrder)
329 {
330     ParallelLinkProgramParams params(compileLinkOrder);
331     params.eglParameters = VULKAN();
332     params.disable(Feature::EnableParallelCompileAndLink);
333     return params;
334 }
335 
336 // Test parallel link performance
TEST_P(ParallelLinkProgramBenchmark,Run)337 TEST_P(ParallelLinkProgramBenchmark, Run)
338 {
339     run();
340 }
341 
342 ANGLE_INSTANTIATE_TEST(
343     ParallelLinkProgramBenchmark,
344     ParallelLinkProgramD3D11Params(CompileLinkOrder::AllCompilesFirst),
345     ParallelLinkProgramD3D11Params(CompileLinkOrder::Interleaved),
346     ParallelLinkProgramMetalParams(CompileLinkOrder::AllCompilesFirst),
347     ParallelLinkProgramMetalParams(CompileLinkOrder::Interleaved),
348     ParallelLinkProgramOpenGLOrGLESParams(CompileLinkOrder::AllCompilesFirst),
349     ParallelLinkProgramOpenGLOrGLESParams(CompileLinkOrder::Interleaved),
350     ParallelLinkProgramVulkanParams(CompileLinkOrder::AllCompilesFirst),
351     ParallelLinkProgramVulkanParams(CompileLinkOrder::Interleaved),
352     ParallelLinkProgramVulkanParams(CompileLinkOrder::InterleavedAndImmediateQuery),
353     SerialLinkProgramVulkanParams(CompileLinkOrder::AllCompilesFirst),
354     SerialLinkProgramVulkanParams(CompileLinkOrder::Interleaved),
355     SerialLinkProgramVulkanParams(CompileLinkOrder::InterleavedAndImmediateQuery));
356 
357 }  // anonymous namespace
358