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 "media/etc2bc_rgba8.inc"
17 #include "test_utils/gl_raii.h"
18 #include "util/shader_utils.h"
19
20 using namespace angle;
21
22 namespace
23 {
24 constexpr unsigned int kIterationsPerStep = 2;
25
26 struct TextureUploadParams final : public RenderTestParams
27 {
TextureUploadParams__anona0f4d5780111::TextureUploadParams28 TextureUploadParams()
29 {
30 iterationsPerStep = kIterationsPerStep;
31 trackGpuTime = true;
32
33 baseSize = 1024;
34 subImageSize = 64;
35
36 webgl = false;
37 }
38
39 std::string story() const override;
40
41 GLsizei baseSize;
42 GLsizei subImageSize;
43
44 bool webgl;
45 };
46
operator <<(std::ostream & os,const TextureUploadParams & params)47 std::ostream &operator<<(std::ostream &os, const TextureUploadParams ¶ms)
48 {
49 os << params.backendAndStory().substr(1);
50 return os;
51 }
52
story() const53 std::string TextureUploadParams::story() const
54 {
55 std::stringstream strstr;
56
57 strstr << RenderTestParams::story();
58
59 if (webgl)
60 {
61 strstr << "_webgl";
62 }
63
64 return strstr.str();
65 }
66
67 class TextureUploadBenchmarkBase : public ANGLERenderTest,
68 public ::testing::WithParamInterface<TextureUploadParams>
69 {
70 public:
71 TextureUploadBenchmarkBase(const char *benchmarkName);
72
73 void initializeBenchmark() override;
74 void destroyBenchmark() override;
75
76 protected:
77 void initShaders();
78
79 GLuint mProgram = 0;
80 GLint mPositionLoc = -1;
81 GLint mSamplerLoc = -1;
82 GLuint mTexture = 0;
83 std::vector<float> mTextureData;
84 };
85
86 class TextureUploadSubImageBenchmark : public TextureUploadBenchmarkBase
87 {
88 public:
TextureUploadSubImageBenchmark()89 TextureUploadSubImageBenchmark() : TextureUploadBenchmarkBase("TexSubImage")
90 {
91 addExtensionPrerequisite("GL_EXT_texture_storage");
92
93 if (IsLinux() && IsIntel() &&
94 GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
95 {
96 skipTest("http://anglebug.com/42264836");
97 }
98 }
99
initializeBenchmark()100 void initializeBenchmark() override
101 {
102 TextureUploadBenchmarkBase::initializeBenchmark();
103
104 const auto ¶ms = GetParam();
105 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
106 }
107
108 void drawBenchmark() override;
109 };
110
111 class TextureUploadFullMipBenchmark : public TextureUploadBenchmarkBase
112 {
113 public:
TextureUploadFullMipBenchmark()114 TextureUploadFullMipBenchmark() : TextureUploadBenchmarkBase("TextureUpload") {}
115
116 void drawBenchmark() override;
117 };
118
119 class PBOSubImageBenchmark : public TextureUploadBenchmarkBase
120 {
121 public:
PBOSubImageBenchmark()122 PBOSubImageBenchmark() : TextureUploadBenchmarkBase("PBO") {}
123
initializeBenchmark()124 void initializeBenchmark() override
125 {
126 TextureUploadBenchmarkBase::initializeBenchmark();
127
128 const auto ¶ms = GetParam();
129
130 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
131
132 glGenBuffers(1, &mPBO);
133 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
134 glBufferData(GL_PIXEL_UNPACK_BUFFER, params.baseSize * params.baseSize * 4,
135 mTextureData.data(), GL_STREAM_DRAW);
136 }
137
destroyBenchmark()138 void destroyBenchmark()
139 {
140 TextureUploadBenchmarkBase::destroyBenchmark();
141 glDeleteBuffers(1, &mPBO);
142 }
143
144 void drawBenchmark() override;
145
146 private:
147 GLuint mPBO;
148 };
149
150 class PBOCompressedSubImageBenchmark : public TextureUploadBenchmarkBase
151 {
152 public:
PBOCompressedSubImageBenchmark()153 PBOCompressedSubImageBenchmark() : TextureUploadBenchmarkBase("PBOCompressed") {}
154
initializeBenchmark()155 void initializeBenchmark() override
156 {
157 TextureUploadBenchmarkBase::initializeBenchmark();
158
159 const auto ¶ms = GetParam();
160
161 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB8_ETC2, params.baseSize,
162 params.baseSize);
163
164 glGenBuffers(1, &mPBO);
165 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
166 glBufferData(GL_PIXEL_UNPACK_BUFFER, params.subImageSize * params.subImageSize / 2,
167 mTextureData.data(), GL_STREAM_DRAW);
168 }
169
destroyBenchmark()170 void destroyBenchmark()
171 {
172 TextureUploadBenchmarkBase::destroyBenchmark();
173 glDeleteBuffers(1, &mPBO);
174 }
175
176 void drawBenchmark() override;
177
178 private:
179 GLuint mPBO;
180 };
181
182 class TextureUploadETC2TranscodingBenchmark : public TextureUploadBenchmarkBase
183 {
184 public:
TextureUploadETC2TranscodingBenchmark()185 TextureUploadETC2TranscodingBenchmark() : TextureUploadBenchmarkBase("ETC2Transcoding") {}
186
initializeBenchmark()187 void initializeBenchmark() override
188 {
189 static GLsizei kMaxTextureSize = 2048;
190 static GLsizei kMinTextureSize = 4;
191 const auto ¶ms = GetParam();
192 ASSERT(params.baseSize <= kMaxTextureSize && params.baseSize >= kMinTextureSize);
193 initShaders();
194 ASSERT_GL_NO_ERROR();
195
196 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
197 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
198 ASSERT_GL_NO_ERROR();
199 mLevels = (int)ceilf(log2(params.baseSize));
200 mTextureBaseLevelSize = 1 << (mLevels - 1);
201 // since we testing ETC texture, the minimum block size is 4.
202 mLevels -= 2;
203 ASSERT_TRUE(mLevels > 0);
204
205 mTextures.resize(params.iterationsPerStep);
206 glGenTextures(params.iterationsPerStep, mTextures.data());
207 for (unsigned int i = 0; i < params.iterationsPerStep; ++i)
208 {
209 glBindTexture(GL_TEXTURE_CUBE_MAP, mTextures[i]);
210 glTexStorage2D(GL_TEXTURE_CUBE_MAP, mLevels, GL_COMPRESSED_RGB8_ETC2,
211 mTextureBaseLevelSize, mTextureBaseLevelSize);
212 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
213 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
214 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
215 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
216 }
217 glActiveTexture(GL_TEXTURE0);
218 ASSERT_GL_NO_ERROR();
219 }
220
destroyBenchmark()221 void destroyBenchmark()
222 {
223 TextureUploadBenchmarkBase::destroyBenchmark();
224 glDeleteTextures(static_cast<GLsizei>(mTextures.size()), mTextures.data());
225 }
226
227 void drawBenchmark() override;
228
229 void initShaders();
230
231 public:
232 GLsizei mTextureBaseLevelSize;
233 GLsizei mLevels;
234 std::vector<GLuint> mTextures;
235 };
236
initShaders()237 void TextureUploadETC2TranscodingBenchmark::initShaders()
238 {
239 constexpr char kVS[] = R"(#version 300 es
240 in vec4 a_position;
241 void main()
242 {
243 gl_Position = a_position;
244 })";
245
246 constexpr char kFS[] = R"(#version 300 es
247 precision mediump float;
248 uniform samplerCube texCube;
249 out vec4 FragColor;
250 void main()
251 {
252 FragColor = texture(texCube, vec3(1.0f, 1.0f, 1.0f));
253 })";
254
255 mProgram = CompileProgram(kVS, kFS);
256 ASSERT_NE(0u, mProgram);
257
258 mPositionLoc = glGetAttribLocation(mProgram, "a_position");
259 mSamplerLoc = glGetUniformLocation(mProgram, "texCube");
260 glUseProgram(mProgram);
261 glUniform1i(mSamplerLoc, 0);
262
263 glDisable(GL_DEPTH_TEST);
264
265 ASSERT_GL_NO_ERROR();
266 }
267
drawBenchmark()268 void TextureUploadETC2TranscodingBenchmark::drawBenchmark()
269 {
270 const auto ¶ms = GetParam();
271 startGpuTimer();
272 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
273 {
274 glBindTexture(GL_TEXTURE_CUBE_MAP, mTextures[iteration]);
275 GLsizei size = mTextureBaseLevelSize;
276 for (GLint level = 0; level < mLevels; ++level)
277 {
278 for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
279 face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; ++face)
280 {
281 glCompressedTexSubImage2D(face, level, 0, 0, size, size, GL_COMPRESSED_RGB8_ETC2,
282 size / 4 * size / 4 * 8, garden_etc2_rgba8);
283 }
284 size >>= 1;
285 }
286 // Perform a draw just so the texture data is flushed. With the position attributes not
287 // set, a constant default value is used, resulting in a very cheap draw.
288 glDrawArrays(GL_TRIANGLES, 0, 3);
289 }
290 stopGpuTimer();
291 }
292
TextureUploadBenchmarkBase(const char * benchmarkName)293 TextureUploadBenchmarkBase::TextureUploadBenchmarkBase(const char *benchmarkName)
294 : ANGLERenderTest(benchmarkName, GetParam())
295 {
296 setWebGLCompatibilityEnabled(GetParam().webgl);
297 setRobustResourceInit(GetParam().webgl);
298
299 if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
300 {
301 skipTest("http://crbug.com/945415 Crashes on nvidia+d3d11");
302 }
303 }
304
initializeBenchmark()305 void TextureUploadBenchmarkBase::initializeBenchmark()
306 {
307 const auto ¶ms = GetParam();
308
309 initShaders();
310 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
311 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
312
313 glActiveTexture(GL_TEXTURE0);
314 glGenTextures(1, &mTexture);
315 glBindTexture(GL_TEXTURE_2D, mTexture);
316
317 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
318 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
319 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
320 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
321
322 ASSERT_TRUE(params.baseSize >= params.subImageSize);
323 mTextureData.resize(params.baseSize * params.baseSize * 4, 0.5);
324
325 ASSERT_GL_NO_ERROR();
326 }
327
initShaders()328 void TextureUploadBenchmarkBase::initShaders()
329 {
330 constexpr char kVS[] = R"(attribute vec4 a_position;
331 void main()
332 {
333 gl_Position = a_position;
334 })";
335
336 constexpr char kFS[] = R"(precision mediump float;
337 uniform sampler2D s_texture;
338 void main()
339 {
340 gl_FragColor = texture2D(s_texture, vec2(0, 0));
341 })";
342
343 mProgram = CompileProgram(kVS, kFS);
344 ASSERT_NE(0u, mProgram);
345
346 mPositionLoc = glGetAttribLocation(mProgram, "a_position");
347 mSamplerLoc = glGetUniformLocation(mProgram, "s_texture");
348 glUseProgram(mProgram);
349 glUniform1i(mSamplerLoc, 0);
350
351 glDisable(GL_DEPTH_TEST);
352
353 ASSERT_GL_NO_ERROR();
354 }
355
destroyBenchmark()356 void TextureUploadBenchmarkBase::destroyBenchmark()
357 {
358 glDeleteTextures(1, &mTexture);
359 glDeleteProgram(mProgram);
360 }
361
drawBenchmark()362 void TextureUploadSubImageBenchmark::drawBenchmark()
363 {
364 const auto ¶ms = GetParam();
365
366 startGpuTimer();
367 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
368 {
369 glTexSubImage2D(GL_TEXTURE_2D, 0, rand() % (params.baseSize - params.subImageSize),
370 rand() % (params.baseSize - params.subImageSize), params.subImageSize,
371 params.subImageSize, GL_RGBA, GL_UNSIGNED_BYTE, mTextureData.data());
372
373 // Perform a draw just so the texture data is flushed. With the position attributes not
374 // set, a constant default value is used, resulting in a very cheap draw.
375 glDrawArrays(GL_TRIANGLES, 0, 3);
376 }
377 stopGpuTimer();
378
379 ASSERT_GL_NO_ERROR();
380 }
381
drawBenchmark()382 void TextureUploadFullMipBenchmark::drawBenchmark()
383 {
384 const auto ¶ms = GetParam();
385
386 startGpuTimer();
387 for (size_t it = 0; it < params.iterationsPerStep; ++it)
388 {
389 // Stage data for all mips
390 GLint mip = 0;
391 for (GLsizei levelSize = params.baseSize; levelSize > 0; levelSize >>= 1)
392 {
393 glTexImage2D(GL_TEXTURE_2D, mip++, GL_RGBA, levelSize, levelSize, 0, GL_RGBA,
394 GL_UNSIGNED_BYTE, mTextureData.data());
395 }
396
397 // Perform a draw just so the texture data is flushed. With the position attributes not
398 // set, a constant default value is used, resulting in a very cheap draw.
399 glDrawArrays(GL_TRIANGLES, 0, 3);
400 }
401 stopGpuTimer();
402
403 ASSERT_GL_NO_ERROR();
404 }
405
drawBenchmark()406 void PBOSubImageBenchmark::drawBenchmark()
407 {
408 const auto ¶ms = GetParam();
409
410 startGpuTimer();
411 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
412 {
413 glTexSubImage2D(GL_TEXTURE_2D, 0, rand() % (params.baseSize - params.subImageSize),
414 rand() % (params.baseSize - params.subImageSize), params.subImageSize,
415 params.subImageSize, GL_RGBA, GL_UNSIGNED_BYTE, 0);
416
417 // Perform a draw just so the texture data is flushed. With the position attributes not
418 // set, a constant default value is used, resulting in a very cheap draw.
419 glDrawArrays(GL_TRIANGLES, 0, 3);
420 }
421 stopGpuTimer();
422
423 ASSERT_GL_NO_ERROR();
424 }
425
drawBenchmark()426 void PBOCompressedSubImageBenchmark::drawBenchmark()
427 {
428 const auto ¶ms = GetParam();
429
430 startGpuTimer();
431 for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
432 {
433 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, params.subImageSize, params.subImageSize,
434 GL_COMPRESSED_RGB8_ETC2,
435 params.subImageSize * params.subImageSize / 2, 0);
436
437 // Perform a draw just so the texture data is flushed. With the position attributes not
438 // set, a constant default value is used, resulting in a very cheap draw.
439 glDrawArrays(GL_TRIANGLES, 0, 3);
440 }
441 stopGpuTimer();
442
443 ASSERT_GL_NO_ERROR();
444 }
445
D3D11Params(bool webglCompat)446 TextureUploadParams D3D11Params(bool webglCompat)
447 {
448 TextureUploadParams params;
449 params.eglParameters = egl_platform::D3D11();
450 params.webgl = webglCompat;
451 return params;
452 }
453
MetalParams(bool webglCompat)454 TextureUploadParams MetalParams(bool webglCompat)
455 {
456 TextureUploadParams params;
457 params.eglParameters = egl_platform::METAL();
458 params.webgl = webglCompat;
459 return params;
460 }
461
OpenGLOrGLESParams(bool webglCompat)462 TextureUploadParams OpenGLOrGLESParams(bool webglCompat)
463 {
464 TextureUploadParams params;
465 params.eglParameters = egl_platform::OPENGL_OR_GLES();
466 params.webgl = webglCompat;
467 return params;
468 }
469
VulkanParams(bool webglCompat)470 TextureUploadParams VulkanParams(bool webglCompat)
471 {
472 TextureUploadParams params;
473 params.eglParameters = egl_platform::VULKAN();
474 params.webgl = webglCompat;
475 return params;
476 }
477
ES3VulkanParams(bool webglCompat)478 TextureUploadParams ES3VulkanParams(bool webglCompat)
479 {
480 TextureUploadParams params;
481 params.eglParameters = egl_platform::VULKAN();
482 params.webgl = webglCompat;
483 params.majorVersion = 3;
484 params.minorVersion = 0;
485 params.enable(Feature::SupportsComputeTranscodeEtcToBc);
486 params.iterationsPerStep = 16;
487 params.baseSize = 256;
488 return params;
489 }
490
MetalPBOParams(GLsizei baseSize,GLsizei subImageSize)491 TextureUploadParams MetalPBOParams(GLsizei baseSize, GLsizei subImageSize)
492 {
493 TextureUploadParams params;
494 params.eglParameters = egl_platform::METAL();
495 params.webgl = false;
496 params.trackGpuTime = false;
497 params.baseSize = baseSize;
498 params.subImageSize = subImageSize;
499 return params;
500 }
501
VulkanPBOParams(GLsizei baseSize,GLsizei subImageSize)502 TextureUploadParams VulkanPBOParams(GLsizei baseSize, GLsizei subImageSize)
503 {
504 TextureUploadParams params;
505 params.eglParameters = egl_platform::VULKAN();
506 params.webgl = false;
507 params.trackGpuTime = false;
508 params.baseSize = baseSize;
509 params.subImageSize = subImageSize;
510 return params;
511 }
512
ES3OpenGLPBOParams(GLsizei baseSize,GLsizei subImageSize)513 TextureUploadParams ES3OpenGLPBOParams(GLsizei baseSize, GLsizei subImageSize)
514 {
515 TextureUploadParams params;
516 params.eglParameters = egl_platform::OPENGL();
517 params.majorVersion = 3;
518 params.minorVersion = 0;
519 params.webgl = false;
520 params.trackGpuTime = false;
521 params.baseSize = baseSize;
522 params.subImageSize = subImageSize;
523 return params;
524 }
525
526 } // anonymous namespace
527
528 // Test etc to bc transcoding performance.
TEST_P(TextureUploadETC2TranscodingBenchmark,Run)529 TEST_P(TextureUploadETC2TranscodingBenchmark, Run)
530 {
531 run();
532 }
533
TEST_P(TextureUploadSubImageBenchmark,Run)534 TEST_P(TextureUploadSubImageBenchmark, Run)
535 {
536 run();
537 }
538
TEST_P(TextureUploadFullMipBenchmark,Run)539 TEST_P(TextureUploadFullMipBenchmark, Run)
540 {
541 run();
542 }
543
TEST_P(PBOSubImageBenchmark,Run)544 TEST_P(PBOSubImageBenchmark, Run)
545 {
546 run();
547 }
548
TEST_P(PBOCompressedSubImageBenchmark,Run)549 TEST_P(PBOCompressedSubImageBenchmark, Run)
550 {
551 run();
552 }
553
554 using namespace params;
555
556 ANGLE_INSTANTIATE_TEST(TextureUploadSubImageBenchmark,
557 D3D11Params(false),
558 D3D11Params(true),
559 MetalParams(false),
560 MetalParams(true),
561 OpenGLOrGLESParams(false),
562 OpenGLOrGLESParams(true),
563 VulkanParams(false),
564 NullDevice(VulkanParams(false)),
565 VulkanParams(true));
566
567 ANGLE_INSTANTIATE_TEST(TextureUploadETC2TranscodingBenchmark, ES3VulkanParams(false));
568
569 ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark,
570 D3D11Params(false),
571 D3D11Params(true),
572 MetalParams(false),
573 MetalParams(true),
574 OpenGLOrGLESParams(false),
575 OpenGLOrGLESParams(true),
576 VulkanParams(false),
577 VulkanParams(true));
578
579 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PBOSubImageBenchmark);
580 ANGLE_INSTANTIATE_TEST(PBOSubImageBenchmark,
581 ES3OpenGLPBOParams(1024, 128),
582 MetalPBOParams(1024, 128),
583 VulkanPBOParams(1024, 128));
584
585 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PBOCompressedSubImageBenchmark);
586 ANGLE_INSTANTIATE_TEST(PBOCompressedSubImageBenchmark,
587 ES3OpenGLPBOParams(128, 128),
588 MetalPBOParams(128, 128),
589 VulkanPBOParams(128, 128));
590