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__anonc15d59220111::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
initializeBenchmark()93 void initializeBenchmark() override
94 {
95 TextureUploadBenchmarkBase::initializeBenchmark();
96
97 const auto ¶ms = GetParam();
98 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
99 }
100
101 void drawBenchmark() override;
102 };
103
104 class TextureUploadFullMipBenchmark : public TextureUploadBenchmarkBase
105 {
106 public:
TextureUploadFullMipBenchmark()107 TextureUploadFullMipBenchmark() : TextureUploadBenchmarkBase("TextureUpload") {}
108
109 void drawBenchmark() override;
110 };
111
112 class PBOSubImageBenchmark : public TextureUploadBenchmarkBase
113 {
114 public:
PBOSubImageBenchmark()115 PBOSubImageBenchmark() : TextureUploadBenchmarkBase("PBO") {}
116
initializeBenchmark()117 void initializeBenchmark() override
118 {
119 TextureUploadBenchmarkBase::initializeBenchmark();
120
121 const auto ¶ms = GetParam();
122
123 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
124
125 glGenBuffers(1, &mPBO);
126 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
127 glBufferData(GL_PIXEL_UNPACK_BUFFER, params.baseSize * params.baseSize * 4,
128 mTextureData.data(), GL_STREAM_DRAW);
129 }
130
destroyBenchmark()131 void destroyBenchmark()
132 {
133 TextureUploadBenchmarkBase::destroyBenchmark();
134 glDeleteBuffers(1, &mPBO);
135 }
136
137 void drawBenchmark() override;
138
139 private:
140 GLuint mPBO;
141 };
142
143 class PBOCompressedSubImageBenchmark : public TextureUploadBenchmarkBase
144 {
145 public:
PBOCompressedSubImageBenchmark()146 PBOCompressedSubImageBenchmark() : TextureUploadBenchmarkBase("PBOCompressed") {}
147
initializeBenchmark()148 void initializeBenchmark() override
149 {
150 TextureUploadBenchmarkBase::initializeBenchmark();
151
152 const auto ¶ms = GetParam();
153
154 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB8_ETC2, params.baseSize,
155 params.baseSize);
156
157 glGenBuffers(1, &mPBO);
158 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
159 glBufferData(GL_PIXEL_UNPACK_BUFFER, params.subImageSize * params.subImageSize / 2,
160 mTextureData.data(), GL_STREAM_DRAW);
161 }
162
destroyBenchmark()163 void destroyBenchmark()
164 {
165 TextureUploadBenchmarkBase::destroyBenchmark();
166 glDeleteBuffers(1, &mPBO);
167 }
168
169 void drawBenchmark() override;
170
171 private:
172 GLuint mPBO;
173 };
174
TextureUploadBenchmarkBase(const char * benchmarkName)175 TextureUploadBenchmarkBase::TextureUploadBenchmarkBase(const char *benchmarkName)
176 : ANGLERenderTest(benchmarkName, GetParam())
177 {
178 setWebGLCompatibilityEnabled(GetParam().webgl);
179 setRobustResourceInit(GetParam().webgl);
180
181 // Crashes on nvidia+d3d11. http://crbug.com/945415
182 if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
183 {
184 mSkipTest = true;
185 }
186 }
187
initializeBenchmark()188 void TextureUploadBenchmarkBase::initializeBenchmark()
189 {
190 const auto ¶ms = GetParam();
191
192 initShaders();
193 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
194 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
195
196 if (params.webgl)
197 {
198 glRequestExtensionANGLE("GL_EXT_disjoint_timer_query");
199 }
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
OpenGLOrGLESParams(bool webglCompat)342 TextureUploadParams OpenGLOrGLESParams(bool webglCompat)
343 {
344 TextureUploadParams params;
345 params.eglParameters = egl_platform::OPENGL_OR_GLES();
346 params.webgl = webglCompat;
347 return params;
348 }
349
VulkanParams(bool webglCompat)350 TextureUploadParams VulkanParams(bool webglCompat)
351 {
352 TextureUploadParams params;
353 params.eglParameters = egl_platform::VULKAN();
354 params.webgl = webglCompat;
355 return params;
356 }
357
VulkanPBOParams(GLsizei baseSize,GLsizei subImageSize)358 TextureUploadParams VulkanPBOParams(GLsizei baseSize, GLsizei subImageSize)
359 {
360 TextureUploadParams params;
361 params.eglParameters = egl_platform::VULKAN();
362 params.webgl = false;
363 params.trackGpuTime = false;
364 params.baseSize = baseSize;
365 params.subImageSize = subImageSize;
366 return params;
367 }
368
ES3OpenGLPBOParams(GLsizei baseSize,GLsizei subImageSize)369 TextureUploadParams ES3OpenGLPBOParams(GLsizei baseSize, GLsizei subImageSize)
370 {
371 TextureUploadParams params;
372 params.eglParameters = egl_platform::OPENGL();
373 params.majorVersion = 3;
374 params.minorVersion = 0;
375 params.webgl = false;
376 params.trackGpuTime = false;
377 params.baseSize = baseSize;
378 params.subImageSize = subImageSize;
379 return params;
380 }
381
382 } // anonymous namespace
383
TEST_P(TextureUploadSubImageBenchmark,Run)384 TEST_P(TextureUploadSubImageBenchmark, Run)
385 {
386 run();
387 }
388
TEST_P(TextureUploadFullMipBenchmark,Run)389 TEST_P(TextureUploadFullMipBenchmark, Run)
390 {
391 run();
392 }
393
TEST_P(PBOSubImageBenchmark,Run)394 TEST_P(PBOSubImageBenchmark, Run)
395 {
396 run();
397 }
398
TEST_P(PBOCompressedSubImageBenchmark,Run)399 TEST_P(PBOCompressedSubImageBenchmark, Run)
400 {
401 run();
402 }
403
404 using namespace params;
405
406 ANGLE_INSTANTIATE_TEST(TextureUploadSubImageBenchmark,
407 D3D11Params(false),
408 D3D11Params(true),
409 OpenGLOrGLESParams(false),
410 OpenGLOrGLESParams(true),
411 VulkanParams(false),
412 NullDevice(VulkanParams(false)),
413 VulkanParams(true));
414
415 ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark,
416 D3D11Params(false),
417 D3D11Params(true),
418 OpenGLOrGLESParams(false),
419 OpenGLOrGLESParams(true),
420 VulkanParams(false),
421 VulkanParams(true));
422
423 ANGLE_INSTANTIATE_TEST(PBOSubImageBenchmark,
424 ES3OpenGLPBOParams(1024, 128),
425 VulkanPBOParams(1024, 128));
426
427 ANGLE_INSTANTIATE_TEST(PBOCompressedSubImageBenchmark,
428 ES3OpenGLPBOParams(128, 128),
429 VulkanPBOParams(128, 128));
430