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__anon20266::final27 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 ¶ms)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 // Crashes on nvidia+d3d11. http://crbug.com/945415
128 if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
129 {
130 mSkipTest = true;
131 }
132
133 // Fails on Windows7 NVIDIA Vulkan, presumably due to old drivers. http://crbug.com/1096510
134 if (IsWindows7() && IsNVIDIA() &&
135 GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
136 {
137 mSkipTest = true;
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
OpenGLOrGLESParams(bool webglCompat, bool singleIteration)289 GenerateMipmapParams OpenGLOrGLESParams(bool webglCompat, bool singleIteration)
290 {
291 GenerateMipmapParams params;
292 params.eglParameters = egl_platform::OPENGL_OR_GLES();
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
VulkanParams(bool webglCompat, bool singleIteration, bool emulatedFormat)303 GenerateMipmapParams VulkanParams(bool webglCompat, bool singleIteration, bool emulatedFormat)
304 {
305 GenerateMipmapParams params;
306 params.eglParameters = egl_platform::VULKAN();
307 params.majorVersion = 3;
308 params.minorVersion = 0;
309 params.webgl = webglCompat;
310 if (emulatedFormat)
311 {
312 params.internalFormat = GL_RGB;
313 }
314 if (singleIteration)
315 {
316 params.iterationsPerStep = 1;
317 }
318 return params;
319 }
320
321 } // anonymous namespace
322
TEST_P(GenerateMipmapBenchmark, Run)323 TEST_P(GenerateMipmapBenchmark, Run)
324 {
325 run();
326 }
327
TEST_P(GenerateMipmapWithRedefineBenchmark, Run)328 TEST_P(GenerateMipmapWithRedefineBenchmark, Run)
329 {
330 run();
331 }
332
333 using namespace params;
334
335 ANGLE_INSTANTIATE_TEST(GenerateMipmapBenchmark,
336 D3D11Params(false, false),
337 D3D11Params(true, false),
338 OpenGLOrGLESParams(false, false),
339 OpenGLOrGLESParams(true, false),
340 VulkanParams(false, false, false),
341 VulkanParams(true, false, false),
342 VulkanParams(false, false, true),
343 VulkanParams(true, false, true));
344
345 ANGLE_INSTANTIATE_TEST(GenerateMipmapWithRedefineBenchmark,
346 D3D11Params(false, true),
347 D3D11Params(true, true),
348 OpenGLOrGLESParams(false, true),
349 OpenGLOrGLESParams(true, true),
350 VulkanParams(false, true, false),
351 VulkanParams(true, true, false),
352 VulkanParams(false, true, true),
353 VulkanParams(true, true, true));
354