1 //
2 // Copyright 2019 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 // TextureUploadBenchmark:
7 // Performance test for uploading texture data.
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 = 2;
24
25 struct TextureUploadParams final : public RenderTestParams
26 {
TextureUploadParams__anon1d676fa70111::TextureUploadParams27 TextureUploadParams()
28 {
29 iterationsPerStep = kIterationsPerStep;
30 trackGpuTime = true;
31
32 baseSize = 1024;
33 subImageSize = 64;
34
35 webgl = false;
36 }
37
38 std::string story() const override;
39
40 GLsizei baseSize;
41 GLsizei subImageSize;
42
43 bool webgl;
44 };
45
operator <<(std::ostream & os,const TextureUploadParams & params)46 std::ostream &operator<<(std::ostream &os, const TextureUploadParams ¶ms)
47 {
48 os << params.backendAndStory().substr(1);
49 return os;
50 }
51
story() const52 std::string TextureUploadParams::story() const
53 {
54 std::stringstream strstr;
55
56 strstr << RenderTestParams::story();
57
58 if (webgl)
59 {
60 strstr << "_webgl";
61 }
62
63 return strstr.str();
64 }
65
66 class TextureUploadBenchmarkBase : public ANGLERenderTest,
67 public ::testing::WithParamInterface<TextureUploadParams>
68 {
69 public:
70 TextureUploadBenchmarkBase(const char *benchmarkName);
71
72 void initializeBenchmark() override;
73 void destroyBenchmark() override;
74
75 protected:
76 void initShaders();
77
78 GLuint mProgram = 0;
79 GLint mPositionLoc = -1;
80 GLint mSamplerLoc = -1;
81 GLuint mTexture = 0;
82 std::vector<float> mTextureData;
83 };
84
85 class TextureUploadSubImageBenchmark : public TextureUploadBenchmarkBase
86 {
87 public:
TextureUploadSubImageBenchmark()88 TextureUploadSubImageBenchmark() : TextureUploadBenchmarkBase("TexSubImage")
89 {
90 addExtensionPrerequisite("GL_EXT_texture_storage");
91
92 if (IsLinux() && IsIntel() &&
93 GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
94 {
95 skipTest("http://anglebug.com/6319");
96 }
97 }
98
initializeBenchmark()99 void initializeBenchmark() override
100 {
101 TextureUploadBenchmarkBase::initializeBenchmark();
102
103 const auto ¶ms = GetParam();
104 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
105 }
106
107 void drawBenchmark() override;
108 };
109
110 class TextureUploadFullMipBenchmark : public TextureUploadBenchmarkBase
111 {
112 public:
TextureUploadFullMipBenchmark()113 TextureUploadFullMipBenchmark() : TextureUploadBenchmarkBase("TextureUpload") {}
114
115 void drawBenchmark() override;
116 };
117
118 class PBOSubImageBenchmark : public TextureUploadBenchmarkBase
119 {
120 public:
PBOSubImageBenchmark()121 PBOSubImageBenchmark() : TextureUploadBenchmarkBase("PBO") {}
122
initializeBenchmark()123 void initializeBenchmark() override
124 {
125 TextureUploadBenchmarkBase::initializeBenchmark();
126
127 const auto ¶ms = GetParam();
128
129 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
130
131 glGenBuffers(1, &mPBO);
132 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
133 glBufferData(GL_PIXEL_UNPACK_BUFFER, params.baseSize * params.baseSize * 4,
134 mTextureData.data(), GL_STREAM_DRAW);
135 }
136
destroyBenchmark()137 void destroyBenchmark()
138 {
139 TextureUploadBenchmarkBase::destroyBenchmark();
140 glDeleteBuffers(1, &mPBO);
141 }
142
143 void drawBenchmark() override;
144
145 private:
146 GLuint mPBO;
147 };
148
149 class PBOCompressedSubImageBenchmark : public TextureUploadBenchmarkBase
150 {
151 public:
PBOCompressedSubImageBenchmark()152 PBOCompressedSubImageBenchmark() : TextureUploadBenchmarkBase("PBOCompressed") {}
153
initializeBenchmark()154 void initializeBenchmark() override
155 {
156 TextureUploadBenchmarkBase::initializeBenchmark();
157
158 const auto ¶ms = GetParam();
159
160 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB8_ETC2, params.baseSize,
161 params.baseSize);
162
163 glGenBuffers(1, &mPBO);
164 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
165 glBufferData(GL_PIXEL_UNPACK_BUFFER, params.subImageSize * params.subImageSize / 2,
166 mTextureData.data(), GL_STREAM_DRAW);
167 }
168
destroyBenchmark()169 void destroyBenchmark()
170 {
171 TextureUploadBenchmarkBase::destroyBenchmark();
172 glDeleteBuffers(1, &mPBO);
173 }
174
175 void drawBenchmark() override;
176
177 private:
178 GLuint mPBO;
179 };
180
TextureUploadBenchmarkBase(const char * benchmarkName)181 TextureUploadBenchmarkBase::TextureUploadBenchmarkBase(const char *benchmarkName)
182 : ANGLERenderTest(benchmarkName, GetParam())
183 {
184 setWebGLCompatibilityEnabled(GetParam().webgl);
185 setRobustResourceInit(GetParam().webgl);
186
187 if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
188 {
189 skipTest("http://crbug.com/945415 Crashes on nvidia+d3d11");
190 }
191 }
192
initializeBenchmark()193 void TextureUploadBenchmarkBase::initializeBenchmark()
194 {
195 const auto ¶ms = GetParam();
196
197 initShaders();
198 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
199 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
200
201 glActiveTexture(GL_TEXTURE0);
202 glGenTextures(1, &mTexture);
203 glBindTexture(GL_TEXTURE_2D, mTexture);
204
205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
209
210 ASSERT_TRUE(params.baseSize >= params.subImageSize);
211 mTextureData.resize(params.baseSize * params.baseSize * 4, 0.5);
212
213 ASSERT_GL_NO_ERROR();
214 }
215
initShaders()216 void TextureUploadBenchmarkBase::initShaders()
217 {
218 constexpr char kVS[] = R"(attribute vec4 a_position;
219 void main()
220 {
221 gl_Position = a_position;
222 })";
223
224 constexpr char kFS[] = R"(precision mediump float;
225 uniform sampler2D s_texture;
226 void main()
227 {
228 gl_FragColor = texture2D(s_texture, vec2(0, 0));
229 })";
230
231 mProgram = CompileProgram(kVS, kFS);
232 ASSERT_NE(0u, mProgram);
233
234 mPositionLoc = glGetAttribLocation(mProgram, "a_position");
235 mSamplerLoc = glGetUniformLocation(mProgram, "s_texture");
236 glUseProgram(mProgram);
237 glUniform1i(mSamplerLoc, 0);
238
239 glDisable(GL_DEPTH_TEST);
240
241 ASSERT_GL_NO_ERROR();
242 }
243
destroyBenchmark()244 void TextureUploadBenchmarkBase::destroyBenchmark()
245 {
246 glDeleteTextures(1, &mTexture);
247 glDeleteProgram(mProgram);
248 }
249
drawBenchmark()250 void TextureUploadSubImageBenchmark::drawBenchmark()
251 {
252 const auto ¶ms = GetParam();
253
254 startGpuTimer();
255 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
256 {
257 glTexSubImage2D(GL_TEXTURE_2D, 0, rand() % (params.baseSize - params.subImageSize),
258 rand() % (params.baseSize - params.subImageSize), params.subImageSize,
259 params.subImageSize, GL_RGBA, GL_UNSIGNED_BYTE, mTextureData.data());
260
261 // Perform a draw just so the texture data is flushed. With the position attributes not
262 // set, a constant default value is used, resulting in a very cheap draw.
263 glDrawArrays(GL_TRIANGLES, 0, 3);
264 }
265 stopGpuTimer();
266
267 ASSERT_GL_NO_ERROR();
268 }
269
drawBenchmark()270 void TextureUploadFullMipBenchmark::drawBenchmark()
271 {
272 const auto ¶ms = GetParam();
273
274 startGpuTimer();
275 for (size_t it = 0; it < params.iterationsPerStep; ++it)
276 {
277 // Stage data for all mips
278 GLint mip = 0;
279 for (GLsizei levelSize = params.baseSize; levelSize > 0; levelSize >>= 1)
280 {
281 glTexImage2D(GL_TEXTURE_2D, mip++, GL_RGBA, levelSize, levelSize, 0, GL_RGBA,
282 GL_UNSIGNED_BYTE, mTextureData.data());
283 }
284
285 // Perform a draw just so the texture data is flushed. With the position attributes not
286 // set, a constant default value is used, resulting in a very cheap draw.
287 glDrawArrays(GL_TRIANGLES, 0, 3);
288 }
289 stopGpuTimer();
290
291 ASSERT_GL_NO_ERROR();
292 }
293
drawBenchmark()294 void PBOSubImageBenchmark::drawBenchmark()
295 {
296 const auto ¶ms = GetParam();
297
298 startGpuTimer();
299 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
300 {
301 glTexSubImage2D(GL_TEXTURE_2D, 0, rand() % (params.baseSize - params.subImageSize),
302 rand() % (params.baseSize - params.subImageSize), params.subImageSize,
303 params.subImageSize, GL_RGBA, GL_UNSIGNED_BYTE, 0);
304
305 // Perform a draw just so the texture data is flushed. With the position attributes not
306 // set, a constant default value is used, resulting in a very cheap draw.
307 glDrawArrays(GL_TRIANGLES, 0, 3);
308 }
309 stopGpuTimer();
310
311 ASSERT_GL_NO_ERROR();
312 }
313
drawBenchmark()314 void PBOCompressedSubImageBenchmark::drawBenchmark()
315 {
316 const auto ¶ms = GetParam();
317
318 startGpuTimer();
319 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
320 {
321 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, params.subImageSize, params.subImageSize,
322 GL_COMPRESSED_RGB8_ETC2,
323 params.subImageSize * params.subImageSize / 2, 0);
324
325 // Perform a draw just so the texture data is flushed. With the position attributes not
326 // set, a constant default value is used, resulting in a very cheap draw.
327 glDrawArrays(GL_TRIANGLES, 0, 3);
328 }
329 stopGpuTimer();
330
331 ASSERT_GL_NO_ERROR();
332 }
333
D3D11Params(bool webglCompat)334 TextureUploadParams D3D11Params(bool webglCompat)
335 {
336 TextureUploadParams params;
337 params.eglParameters = egl_platform::D3D11();
338 params.webgl = webglCompat;
339 return params;
340 }
341
MetalParams(bool webglCompat)342 TextureUploadParams MetalParams(bool webglCompat)
343 {
344 TextureUploadParams params;
345 params.eglParameters = egl_platform::METAL();
346 params.webgl = webglCompat;
347 return params;
348 }
349
OpenGLOrGLESParams(bool webglCompat)350 TextureUploadParams OpenGLOrGLESParams(bool webglCompat)
351 {
352 TextureUploadParams params;
353 params.eglParameters = egl_platform::OPENGL_OR_GLES();
354 params.webgl = webglCompat;
355 return params;
356 }
357
VulkanParams(bool webglCompat)358 TextureUploadParams VulkanParams(bool webglCompat)
359 {
360 TextureUploadParams params;
361 params.eglParameters = egl_platform::VULKAN();
362 params.webgl = webglCompat;
363 return params;
364 }
365
MetalPBOParams(GLsizei baseSize,GLsizei subImageSize)366 TextureUploadParams MetalPBOParams(GLsizei baseSize, GLsizei subImageSize)
367 {
368 TextureUploadParams params;
369 params.eglParameters = egl_platform::METAL();
370 params.webgl = false;
371 params.trackGpuTime = false;
372 params.baseSize = baseSize;
373 params.subImageSize = subImageSize;
374 return params;
375 }
376
VulkanPBOParams(GLsizei baseSize,GLsizei subImageSize)377 TextureUploadParams VulkanPBOParams(GLsizei baseSize, GLsizei subImageSize)
378 {
379 TextureUploadParams params;
380 params.eglParameters = egl_platform::VULKAN();
381 params.webgl = false;
382 params.trackGpuTime = false;
383 params.baseSize = baseSize;
384 params.subImageSize = subImageSize;
385 return params;
386 }
387
ES3OpenGLPBOParams(GLsizei baseSize,GLsizei subImageSize)388 TextureUploadParams ES3OpenGLPBOParams(GLsizei baseSize, GLsizei subImageSize)
389 {
390 TextureUploadParams params;
391 params.eglParameters = egl_platform::OPENGL();
392 params.majorVersion = 3;
393 params.minorVersion = 0;
394 params.webgl = false;
395 params.trackGpuTime = false;
396 params.baseSize = baseSize;
397 params.subImageSize = subImageSize;
398 return params;
399 }
400
401 } // anonymous namespace
402
TEST_P(TextureUploadSubImageBenchmark,Run)403 TEST_P(TextureUploadSubImageBenchmark, Run)
404 {
405 run();
406 }
407
TEST_P(TextureUploadFullMipBenchmark,Run)408 TEST_P(TextureUploadFullMipBenchmark, Run)
409 {
410 run();
411 }
412
TEST_P(PBOSubImageBenchmark,Run)413 TEST_P(PBOSubImageBenchmark, Run)
414 {
415 run();
416 }
417
TEST_P(PBOCompressedSubImageBenchmark,Run)418 TEST_P(PBOCompressedSubImageBenchmark, Run)
419 {
420 run();
421 }
422
423 using namespace params;
424
425 ANGLE_INSTANTIATE_TEST(TextureUploadSubImageBenchmark,
426 D3D11Params(false),
427 D3D11Params(true),
428 MetalParams(false),
429 MetalParams(true),
430 OpenGLOrGLESParams(false),
431 OpenGLOrGLESParams(true),
432 VulkanParams(false),
433 NullDevice(VulkanParams(false)),
434 VulkanParams(true));
435
436 ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark,
437 D3D11Params(false),
438 D3D11Params(true),
439 MetalParams(false),
440 MetalParams(true),
441 OpenGLOrGLESParams(false),
442 OpenGLOrGLESParams(true),
443 VulkanParams(false),
444 VulkanParams(true));
445
446 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PBOSubImageBenchmark);
447 ANGLE_INSTANTIATE_TEST(PBOSubImageBenchmark,
448 ES3OpenGLPBOParams(1024, 128),
449 MetalPBOParams(1024, 128),
450 VulkanPBOParams(1024, 128));
451
452 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PBOCompressedSubImageBenchmark);
453 ANGLE_INSTANTIATE_TEST(PBOCompressedSubImageBenchmark,
454 ES3OpenGLPBOParams(128, 128),
455 MetalPBOParams(128, 128),
456 VulkanPBOParams(128, 128));
457