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 &params)50 std::ostream &operator<<(std::ostream &os, const GenerateMipmapParams &params)
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 &params = 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 &params = 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 &params = 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