1 //
2 // Copyright 2020 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 // GenerateMipmapBenchmark:
7 // Performance test for generating texture mipmaps.
8 //
9
10 #include "ANGLEPerfTest.h"
11
12 #include <iostream>
13 #include <random>
14 #include <sstream>
15
16 #include "test_utils/gl_raii.h"
17 #include "util/shader_utils.h"
18
19 using namespace angle;
20
21 namespace
22 {
23 constexpr unsigned int kIterationsPerStep = 5;
24
25 struct GenerateMipmapParams final : public RenderTestParams
26 {
GenerateMipmapParams__anonb9e554b10111::GenerateMipmapParams27 GenerateMipmapParams()
28 {
29 iterationsPerStep = kIterationsPerStep;
30 trackGpuTime = true;
31
32 textureWidth = 1920;
33 textureHeight = 1080;
34
35 internalFormat = GL_RGBA;
36
37 webgl = false;
38 }
39
40 std::string story() const override;
41
42 GLsizei textureWidth;
43 GLsizei textureHeight;
44
45 GLenum internalFormat;
46
47 bool webgl;
48 };
49
operator <<(std::ostream & os,const GenerateMipmapParams & params)50 std::ostream &operator<<(std::ostream &os, const GenerateMipmapParams ¶ms)
51 {
52 return os << params.backendAndStory().substr(1);
53 }
54
story() const55 std::string GenerateMipmapParams::story() const
56 {
57 std::stringstream strstr;
58
59 strstr << RenderTestParams::story();
60
61 if (webgl)
62 {
63 strstr << "_webgl";
64 }
65
66 if (internalFormat == GL_RGB)
67 {
68 strstr << "_rgb";
69 }
70
71 return strstr.str();
72 }
73
74 template <typename T>
FillWithRandomData(T * storage)75 void FillWithRandomData(T *storage)
76 {
77 for (uint8_t &u : *storage)
78 {
79 u = rand() & 0xFF;
80 }
81 }
82
83 class GenerateMipmapBenchmarkBase : public ANGLERenderTest,
84 public ::testing::WithParamInterface<GenerateMipmapParams>
85 {
86 public:
87 GenerateMipmapBenchmarkBase(const char *benchmarkName);
88
89 void initializeBenchmark() override;
90 void destroyBenchmark() override;
91
92 protected:
93 void initShaders();
94
95 GLuint mProgram = 0;
96 GLuint mTexture = 0;
97
98 std::vector<uint8_t> mTextureData;
99 };
100
101 class GenerateMipmapBenchmark : public GenerateMipmapBenchmarkBase
102 {
103 public:
GenerateMipmapBenchmark()104 GenerateMipmapBenchmark() : GenerateMipmapBenchmarkBase("GenerateMipmap") {}
105
106 void initializeBenchmark() override;
107
108 void drawBenchmark() override;
109 };
110
111 class GenerateMipmapWithRedefineBenchmark : public GenerateMipmapBenchmarkBase
112 {
113 public:
GenerateMipmapWithRedefineBenchmark()114 GenerateMipmapWithRedefineBenchmark()
115 : GenerateMipmapBenchmarkBase("GenerateMipmapWithRedefine")
116 {}
117
118 void drawBenchmark() override;
119 };
120
GenerateMipmapBenchmarkBase(const char * benchmarkName)121 GenerateMipmapBenchmarkBase::GenerateMipmapBenchmarkBase(const char *benchmarkName)
122 : ANGLERenderTest(benchmarkName, GetParam())
123 {
124 setWebGLCompatibilityEnabled(GetParam().webgl);
125 setRobustResourceInit(GetParam().webgl);
126
127 if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
128 {
129 skipTest("http://crbug.com/945415 Crashes on nvidia+d3d11");
130 }
131
132 if (IsWindows7() && IsNVIDIA() &&
133 GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
134 {
135 skipTest(
136 "http://crbug.com/1096510 Fails on Windows7 NVIDIA Vulkan, presumably due to old "
137 "drivers");
138 }
139 }
140
initializeBenchmark()141 void GenerateMipmapBenchmarkBase::initializeBenchmark()
142 {
143 const auto ¶ms = GetParam();
144
145 initShaders();
146 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
147 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
148
149 glGenTextures(1, &mTexture);
150 glBindTexture(GL_TEXTURE_2D, mTexture);
151
152 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
153 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
156
157 mTextureData.resize(params.textureWidth * params.textureHeight * 4);
158 FillWithRandomData(&mTextureData);
159
160 glTexImage2D(GL_TEXTURE_2D, 0, params.internalFormat, params.textureWidth, params.textureHeight,
161 0, params.internalFormat, GL_UNSIGNED_BYTE, mTextureData.data());
162
163 // Perform a draw so the image data is flushed.
164 glDrawArrays(GL_TRIANGLES, 0, 3);
165
166 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
167
168 ASSERT_GL_NO_ERROR();
169 }
170
initShaders()171 void GenerateMipmapBenchmarkBase::initShaders()
172 {
173 constexpr char kVS[] = R"(void main()
174 {
175 gl_Position = vec4(0, 0, 0, 1);
176 })";
177
178 constexpr char kFS[] = R"(precision mediump float;
179 void main()
180 {
181 gl_FragColor = vec4(0);
182 })";
183
184 mProgram = CompileProgram(kVS, kFS);
185 ASSERT_NE(0u, mProgram);
186
187 glUseProgram(mProgram);
188
189 glDisable(GL_DEPTH_TEST);
190
191 ASSERT_GL_NO_ERROR();
192 }
193
destroyBenchmark()194 void GenerateMipmapBenchmarkBase::destroyBenchmark()
195 {
196 glDeleteTextures(1, &mTexture);
197 glDeleteProgram(mProgram);
198 }
199
initializeBenchmark()200 void GenerateMipmapBenchmark::initializeBenchmark()
201 {
202 GenerateMipmapBenchmarkBase::initializeBenchmark();
203
204 // Generate mipmaps once so the texture doesn't need to be redefined.
205 glGenerateMipmap(GL_TEXTURE_2D);
206
207 // Perform a draw so the image data is flushed.
208 glDrawArrays(GL_TRIANGLES, 0, 3);
209 }
210
drawBenchmark()211 void GenerateMipmapBenchmark::drawBenchmark()
212 {
213 const auto ¶ms = GetParam();
214
215 startGpuTimer();
216 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
217 {
218 // Slightly modify the base texture so the mipmap is definitely regenerated.
219 std::array<uint8_t, 4> randomData;
220 FillWithRandomData(&randomData);
221
222 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, params.internalFormat, GL_UNSIGNED_BYTE,
223 randomData.data());
224
225 // Generate mipmaps
226 glGenerateMipmap(GL_TEXTURE_2D);
227
228 // Perform a draw just so the texture data is flushed. With the position attributes not
229 // set, a constant default value is used, resulting in a very cheap draw.
230 glDrawArrays(GL_TRIANGLES, 0, 3);
231 }
232 stopGpuTimer();
233
234 ASSERT_GL_NO_ERROR();
235 }
236
drawBenchmark()237 void GenerateMipmapWithRedefineBenchmark::drawBenchmark()
238 {
239 const auto ¶ms = GetParam();
240
241 // Create a new texture every time, so image redefinition happens every time.
242 GLTexture texture;
243 glBindTexture(GL_TEXTURE_2D, texture);
244
245 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
246 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
247 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
248 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
249
250 glTexImage2D(GL_TEXTURE_2D, 0, params.internalFormat, params.textureWidth, params.textureHeight,
251 0, params.internalFormat, GL_UNSIGNED_BYTE, mTextureData.data());
252
253 // Perform a draw so the image data is flushed.
254 glDrawArrays(GL_TRIANGLES, 0, 3);
255
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
257
258 startGpuTimer();
259
260 // Do a single iteration, otherwise the cost of redefinition is amortized.
261 ASSERT_EQ(params.iterationsPerStep, 1u);
262
263 // Generate mipmaps
264 glGenerateMipmap(GL_TEXTURE_2D);
265
266 // Perform a draw just so the texture data is flushed. With the position attributes not
267 // set, a constant default value is used, resulting in a very cheap draw.
268 glDrawArrays(GL_TRIANGLES, 0, 3);
269
270 stopGpuTimer();
271
272 ASSERT_GL_NO_ERROR();
273 }
274
D3D11Params(bool webglCompat,bool singleIteration)275 GenerateMipmapParams D3D11Params(bool webglCompat, bool singleIteration)
276 {
277 GenerateMipmapParams params;
278 params.eglParameters = egl_platform::D3D11();
279 params.majorVersion = 3;
280 params.minorVersion = 0;
281 params.webgl = webglCompat;
282 if (singleIteration)
283 {
284 params.iterationsPerStep = 1;
285 }
286 return params;
287 }
288
MetalParams(bool webglCompat,bool singleIteration)289 GenerateMipmapParams MetalParams(bool webglCompat, bool singleIteration)
290 {
291 GenerateMipmapParams params;
292 params.eglParameters = egl_platform::METAL();
293 params.majorVersion = 3;
294 params.minorVersion = 0;
295 params.webgl = webglCompat;
296 if (singleIteration)
297 {
298 params.iterationsPerStep = 1;
299 }
300 return params;
301 }
302
OpenGLOrGLESParams(bool webglCompat,bool singleIteration)303 GenerateMipmapParams OpenGLOrGLESParams(bool webglCompat, bool singleIteration)
304 {
305 GenerateMipmapParams params;
306 params.eglParameters = egl_platform::OPENGL_OR_GLES();
307 params.majorVersion = 3;
308 params.minorVersion = 0;
309 params.webgl = webglCompat;
310 if (singleIteration)
311 {
312 params.iterationsPerStep = 1;
313 }
314 return params;
315 }
316
VulkanParams(bool webglCompat,bool singleIteration,bool emulatedFormat)317 GenerateMipmapParams VulkanParams(bool webglCompat, bool singleIteration, bool emulatedFormat)
318 {
319 GenerateMipmapParams params;
320 params.eglParameters = egl_platform::VULKAN();
321 params.majorVersion = 3;
322 params.minorVersion = 0;
323 params.webgl = webglCompat;
324 if (emulatedFormat)
325 {
326 params.internalFormat = GL_RGB;
327 }
328 if (singleIteration)
329 {
330 params.iterationsPerStep = 1;
331 }
332 return params;
333 }
334
335 } // anonymous namespace
336
TEST_P(GenerateMipmapBenchmark,Run)337 TEST_P(GenerateMipmapBenchmark, Run)
338 {
339 run();
340 }
341
TEST_P(GenerateMipmapWithRedefineBenchmark,Run)342 TEST_P(GenerateMipmapWithRedefineBenchmark, Run)
343 {
344 run();
345 }
346
347 using namespace params;
348
349 ANGLE_INSTANTIATE_TEST(GenerateMipmapBenchmark,
350 D3D11Params(false, false),
351 D3D11Params(true, false),
352 MetalParams(false, false),
353 MetalParams(true, false),
354 OpenGLOrGLESParams(false, false),
355 OpenGLOrGLESParams(true, false),
356 VulkanParams(false, false, false),
357 VulkanParams(true, false, false),
358 VulkanParams(false, false, true),
359 VulkanParams(true, false, true));
360
361 ANGLE_INSTANTIATE_TEST(GenerateMipmapWithRedefineBenchmark,
362 D3D11Params(false, true),
363 D3D11Params(true, true),
364 MetalParams(false, true),
365 MetalParams(true, true),
366 OpenGLOrGLESParams(false, true),
367 OpenGLOrGLESParams(true, true),
368 VulkanParams(false, true, false),
369 VulkanParams(true, true, false),
370 VulkanParams(false, true, true),
371 VulkanParams(true, true, true));
372