1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Texture upload performance tests.
22 *
23 * \todo [2012-10-01 pyry]
24 * - Test different pixel unpack alignments
25 * - Use multiple textures
26 * - Trash cache prior to uploading from data ptr
27 *//*--------------------------------------------------------------------*/
28
29 #include "es2pTextureUploadTests.hpp"
30 #include "tcuTexture.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuTestLog.hpp"
33 #include "tcuSurface.hpp"
34 #include "gluTextureUtil.hpp"
35 #include "gluShaderProgram.hpp"
36 #include "gluPixelTransfer.hpp"
37 #include "deStringUtil.hpp"
38 #include "deRandom.hpp"
39 #include "deClock.h"
40 #include "deString.h"
41
42 #include "glsCalibration.hpp"
43
44 #include "glwEnums.hpp"
45 #include "glwFunctions.hpp"
46
47 #include <algorithm>
48 #include <vector>
49
50 namespace deqp
51 {
52 namespace gles2
53 {
54 namespace Performance
55 {
56
57 using std::string;
58 using std::vector;
59 using tcu::IVec4;
60 using tcu::TestLog;
61 using tcu::TextureFormat;
62 using tcu::Vec2;
63 using tcu::Vec3;
64 using tcu::Vec4;
65 using namespace glw; // GL types
66
67 static const int VIEWPORT_SIZE = 64;
68 static const float quadCoords[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
69
70 class TextureUploadCase : public TestCase
71 {
72 public:
73 TextureUploadCase(Context &context, const char *name, const char *description, UploadFunction uploadFunction,
74 uint32_t format, uint32_t type, int texSize);
75 ~TextureUploadCase(void);
76
77 virtual void init(void);
78 void deinit(void);
79
80 virtual IterateResult iterate(void) = 0;
81 void logResults(void);
82
83 protected:
84 UploadFunction m_uploadFunction;
85 uint32_t m_format;
86 uint32_t m_type;
87 int m_texSize;
88 int m_alignment;
89
90 gls::TheilSenCalibrator m_calibrator;
91 glu::ShaderProgram *m_program;
92 uint32_t m_texture;
93 de::Random m_rnd;
94 TestLog &m_log;
95
96 vector<uint8_t> m_texData;
97 };
98
TextureUploadCase(Context & context,const char * name,const char * description,UploadFunction uploadFunction,uint32_t format,uint32_t type,int texSize)99 TextureUploadCase::TextureUploadCase(Context &context, const char *name, const char *description,
100 UploadFunction uploadFunction, uint32_t format, uint32_t type, int texSize)
101 : TestCase(context, tcu::NODETYPE_PERFORMANCE, name, description)
102 , m_uploadFunction(uploadFunction)
103 , m_format(format)
104 , m_type(type)
105 , m_texSize(texSize)
106 , m_alignment(4)
107 , m_calibrator()
108 , m_program(DE_NULL)
109 , m_texture(0)
110 , m_rnd(deStringHash(name))
111 , m_log(context.getTestContext().getLog())
112 {
113 }
114
~TextureUploadCase(void)115 TextureUploadCase::~TextureUploadCase(void)
116 {
117 TextureUploadCase::deinit();
118 }
119
deinit(void)120 void TextureUploadCase::deinit(void)
121 {
122 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
123
124 if (m_program)
125 {
126 delete m_program;
127 m_program = DE_NULL;
128 }
129
130 gl.deleteTextures(1, &m_texture);
131 m_texture = 0;
132
133 m_texData.clear();
134 }
135
init(void)136 void TextureUploadCase::init(void)
137 {
138 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
139 int maxTextureSize;
140 gl.getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
141
142 if (m_texSize > maxTextureSize)
143 {
144 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Unsupported texture size");
145 return;
146 }
147
148 // Create program
149
150 string vertexShaderSource = "";
151 string fragmentShaderSource = "";
152
153 vertexShaderSource.append("precision mediump float;\n"
154 "attribute vec2 a_pos;\n"
155 "varying vec2 v_texCoord;\n"
156 "\n"
157 "void main (void)\n"
158 "{\n"
159 " v_texCoord = a_pos;\n"
160 " gl_Position = vec4(a_pos, 0.5, 1.0);\n"
161 "}\n");
162
163 fragmentShaderSource.append("precision mediump float;\n"
164 "uniform lowp sampler2D u_sampler;\n"
165 "varying vec2 v_texCoord;\n"
166 "\n"
167 "void main (void)\n"
168 "{\n"
169 " gl_FragColor = texture2D(u_sampler, v_texCoord.xy);\n"
170 "}\n");
171
172 DE_ASSERT(!m_program);
173 m_program = new glu::ShaderProgram(m_context.getRenderContext(),
174 glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
175
176 if (!m_program->isOk())
177 {
178 m_log << *m_program;
179 TCU_FAIL("Failed to create shader program (m_programRender)");
180 }
181
182 gl.useProgram(m_program->getProgram());
183
184 // Init GL state
185
186 gl.viewport(0, 0, VIEWPORT_SIZE, VIEWPORT_SIZE);
187 gl.disable(GL_DEPTH_TEST);
188 gl.disable(GL_CULL_FACE);
189 gl.enable(GL_BLEND);
190 gl.blendFunc(GL_ONE, GL_ONE);
191 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
192 gl.clear(GL_COLOR_BUFFER_BIT);
193
194 uint32_t uSampler = gl.getUniformLocation(m_program->getProgram(), "u_sampler");
195 uint32_t aPos = gl.getAttribLocation(m_program->getProgram(), "a_pos");
196 gl.enableVertexAttribArray(aPos);
197 gl.vertexAttribPointer(aPos, 2, GL_FLOAT, GL_FALSE, 0, &quadCoords[0]);
198 gl.uniform1i(uSampler, 0);
199
200 // Create texture
201
202 gl.activeTexture(GL_TEXTURE0);
203 gl.genTextures(1, &m_texture);
204 gl.bindTexture(GL_TEXTURE_2D, m_texture);
205 gl.pixelStorei(GL_UNPACK_ALIGNMENT, m_alignment);
206 gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
207 gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
208 gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
209 gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
210
211 // Prepare texture data
212
213 {
214 const tcu::TextureFormat &texFmt = glu::mapGLTransferFormat(m_format, m_type);
215 int pixelSize = texFmt.getPixelSize();
216 int stride = deAlign32(pixelSize * m_texSize, m_alignment);
217
218 m_texData.resize(stride * m_texSize);
219
220 tcu::PixelBufferAccess access(texFmt, m_texSize, m_texSize, 1, stride, 0, &m_texData[0]);
221
222 tcu::fillWithComponentGradients(access, tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
223 }
224
225 // Do a dry-run to ensure the pipes are hot
226
227 gl.texImage2D(GL_TEXTURE_2D, 0, m_format, m_texSize, m_texSize, 0, m_format, m_type, &m_texData[0]);
228 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
229 gl.finish();
230 }
231
logResults(void)232 void TextureUploadCase::logResults(void)
233 {
234 const gls::MeasureState &measureState = m_calibrator.getMeasureState();
235
236 // Log measurement details
237
238 m_log << TestLog::Section("Measurement details", "Measurement details");
239 m_log << TestLog::Message << "Uploading texture with "
240 << (m_uploadFunction == UPLOAD_TEXIMAGE2D ? "glTexImage2D" : "glTexSubImage2D") << "."
241 << TestLog::EndMessage; // \todo [arttu] Change enum to struct with name included
242 m_log << TestLog::Message << "Texture size = " << m_texSize << "x" << m_texSize << "." << TestLog::EndMessage;
243 m_log << TestLog::Message << "Viewport size = " << VIEWPORT_SIZE << "x" << VIEWPORT_SIZE << "."
244 << TestLog::EndMessage;
245 m_log << TestLog::Message << measureState.numDrawCalls << " upload calls / iteration" << TestLog::EndMessage;
246 m_log << TestLog::EndSection;
247
248 // Log results
249
250 TestLog &log = m_testCtx.getLog();
251 log << TestLog::Section("Results", "Results");
252
253 // Log individual frame durations
254 //for (int i = 0; i < m_calibrator.measureState.numFrames; i++)
255 // m_log << TestLog::Message << "Frame " << i+1 << " duration: \t" << m_calibrator.measureState.frameTimes[i] << " us."<< TestLog::EndMessage;
256
257 std::vector<uint64_t> sortedFrameTimes(measureState.frameTimes.begin(), measureState.frameTimes.end());
258 std::sort(sortedFrameTimes.begin(), sortedFrameTimes.end());
259 vector<uint64_t>::const_iterator first = sortedFrameTimes.begin();
260 vector<uint64_t>::const_iterator last = sortedFrameTimes.end();
261 vector<uint64_t>::const_iterator middle = first + (last - first) / 2;
262
263 uint64_t medianFrameTime = *middle;
264 double medianMTexelsPerSeconds =
265 (double)(m_texSize * m_texSize * measureState.numDrawCalls) / (double)medianFrameTime;
266 double medianTexelDrawDurationNs =
267 (double)medianFrameTime * 1000.0 / (double)(m_texSize * m_texSize * measureState.numDrawCalls);
268
269 uint64_t totalTime = measureState.getTotalTime();
270 int numFrames = (int)measureState.frameTimes.size();
271 int64_t numTexturesDrawn = measureState.numDrawCalls * numFrames;
272 int64_t numPixels = (int64_t)m_texSize * (int64_t)m_texSize * numTexturesDrawn;
273
274 double framesPerSecond = (double)numFrames / ((double)totalTime / 1000000.0);
275 double avgFrameTime = (double)totalTime / (double)numFrames;
276 double avgMTexelsPerSeconds = (double)numPixels / (double)totalTime;
277 double avgTexelDrawDurationNs = (double)totalTime * 1000.0 / (double)numPixels;
278
279 log << TestLog::Float("FramesPerSecond", "Frames per second in measurement\t\t", "Frames/s", QP_KEY_TAG_PERFORMANCE,
280 (float)framesPerSecond);
281 log << TestLog::Float("AverageFrameTime", "Average frame duration in measurement\t", "us", QP_KEY_TAG_PERFORMANCE,
282 (float)avgFrameTime);
283 log << TestLog::Float("AverageTexelPerf", "Average texel upload performance\t\t", "MTex/s", QP_KEY_TAG_PERFORMANCE,
284 (float)avgMTexelsPerSeconds);
285 log << TestLog::Float("AverageTexelTime", "Average texel upload duration\t\t", "ns", QP_KEY_TAG_PERFORMANCE,
286 (float)avgTexelDrawDurationNs);
287 log << TestLog::Float("MedianTexelPerf", "Median texel upload performance\t\t", "MTex/s", QP_KEY_TAG_PERFORMANCE,
288 (float)medianMTexelsPerSeconds);
289 log << TestLog::Float("MedianTexelTime", "Median texel upload duration\t\t", "ns", QP_KEY_TAG_PERFORMANCE,
290 (float)medianTexelDrawDurationNs);
291
292 log << TestLog::EndSection;
293
294 gls::logCalibrationInfo(log, m_calibrator); // Log calibration details
295 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)avgMTexelsPerSeconds, 2).c_str());
296 }
297
298 // Texture upload call case
299
300 class TextureUploadCallCase : public TextureUploadCase
301 {
302 public:
303 TextureUploadCallCase(Context &context, const char *name, const char *description, UploadFunction uploadFunction,
304 uint32_t format, uint32_t type, int texSize);
305 ~TextureUploadCallCase(void);
306
307 IterateResult iterate(void);
308 void render(void);
309 };
310
TextureUploadCallCase(Context & context,const char * name,const char * description,UploadFunction uploadFunction,uint32_t format,uint32_t type,int texSize)311 TextureUploadCallCase::TextureUploadCallCase(Context &context, const char *name, const char *description,
312 UploadFunction uploadFunction, uint32_t format, uint32_t type, int texSize)
313 : TextureUploadCase(context, name, description, uploadFunction, format, type, texSize)
314 {
315 }
316
~TextureUploadCallCase(void)317 TextureUploadCallCase::~TextureUploadCallCase(void)
318 {
319 TextureUploadCase::deinit();
320 }
321
render(void)322 void TextureUploadCallCase::render(void)
323 {
324 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
325
326 // Draw multiple quads to ensure enough workload
327
328 switch (m_uploadFunction)
329 {
330 case UPLOAD_TEXIMAGE2D:
331 gl.texImage2D(GL_TEXTURE_2D, 0, m_format, m_texSize, m_texSize, 0, m_format, m_type, &m_texData[0]);
332 break;
333 case UPLOAD_TEXSUBIMAGE2D:
334 gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_texSize, m_texSize, m_format, m_type, &m_texData[0]);
335 break;
336 default:
337 DE_ASSERT(false);
338 }
339 }
340
iterate(void)341 tcu::TestNode::IterateResult TextureUploadCallCase::iterate(void)
342 {
343 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
344
345 if (m_testCtx.getTestResult() == QP_TEST_RESULT_NOT_SUPPORTED)
346 return STOP;
347
348 for (;;)
349 {
350 gls::TheilSenCalibrator::State state = m_calibrator.getState();
351
352 if (state == gls::TheilSenCalibrator::STATE_MEASURE)
353 {
354 int numCalls = m_calibrator.getCallCount();
355 uint64_t startTime = deGetMicroseconds();
356
357 for (int i = 0; i < numCalls; i++)
358 render();
359
360 gl.finish();
361
362 uint64_t endTime = deGetMicroseconds();
363 uint64_t duration = endTime - startTime;
364
365 m_calibrator.recordIteration(duration);
366 }
367 else if (state == gls::TheilSenCalibrator::STATE_RECOMPUTE_PARAMS)
368 {
369 m_calibrator.recomputeParameters();
370 }
371 else
372 {
373 DE_ASSERT(state == gls::TheilSenCalibrator::STATE_FINISHED);
374 break;
375 }
376
377 // Touch watchdog between iterations to avoid timeout.
378 {
379 qpWatchDog *dog = m_testCtx.getWatchDog();
380 if (dog)
381 qpWatchDog_touch(dog);
382 }
383 }
384
385 GLU_EXPECT_NO_ERROR(gl.getError(), "iterate");
386 logResults();
387 return STOP;
388 }
389
390 // Texture upload and draw case
391
392 class TextureUploadAndDrawCase : public TextureUploadCase
393 {
394 public:
395 TextureUploadAndDrawCase(Context &context, const char *name, const char *description, UploadFunction uploadFunction,
396 uint32_t format, uint32_t type, int texSize);
397 ~TextureUploadAndDrawCase(void);
398
399 IterateResult iterate(void);
400 void render(void);
401
402 private:
403 bool m_lastIterationRender;
404 uint64_t m_renderStart;
405 };
406
TextureUploadAndDrawCase(Context & context,const char * name,const char * description,UploadFunction uploadFunction,uint32_t format,uint32_t type,int texSize)407 TextureUploadAndDrawCase::TextureUploadAndDrawCase(Context &context, const char *name, const char *description,
408 UploadFunction uploadFunction, uint32_t format, uint32_t type,
409 int texSize)
410 : TextureUploadCase(context, name, description, uploadFunction, format, type, texSize)
411 , m_lastIterationRender(false)
412 , m_renderStart(0)
413 {
414 }
415
~TextureUploadAndDrawCase(void)416 TextureUploadAndDrawCase::~TextureUploadAndDrawCase(void)
417 {
418 TextureUploadCase::deinit();
419 }
420
render(void)421 void TextureUploadAndDrawCase::render(void)
422 {
423 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
424
425 // Draw multiple quads to ensure enough workload
426
427 switch (m_uploadFunction)
428 {
429 case UPLOAD_TEXIMAGE2D:
430 gl.texImage2D(GL_TEXTURE_2D, 0, m_format, m_texSize, m_texSize, 0, m_format, m_type, &m_texData[0]);
431 break;
432 case UPLOAD_TEXSUBIMAGE2D:
433 gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_texSize, m_texSize, m_format, m_type, &m_texData[0]);
434 break;
435 default:
436 DE_ASSERT(false);
437 }
438
439 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
440 }
441
iterate(void)442 tcu::TestNode::IterateResult TextureUploadAndDrawCase::iterate(void)
443 {
444 if (m_testCtx.getTestResult() == QP_TEST_RESULT_NOT_SUPPORTED)
445 return STOP;
446
447 if (m_lastIterationRender && (m_calibrator.getState() == gls::TheilSenCalibrator::STATE_MEASURE))
448 {
449 uint64_t curTime = deGetMicroseconds();
450 m_calibrator.recordIteration(curTime - m_renderStart);
451 }
452
453 gls::TheilSenCalibrator::State state = m_calibrator.getState();
454
455 if (state == gls::TheilSenCalibrator::STATE_MEASURE)
456 {
457 // Render
458 int numCalls = m_calibrator.getCallCount();
459
460 m_renderStart = deGetMicroseconds();
461 m_lastIterationRender = true;
462
463 for (int i = 0; i < numCalls; i++)
464 render();
465
466 return CONTINUE;
467 }
468 else if (state == gls::TheilSenCalibrator::STATE_RECOMPUTE_PARAMS)
469 {
470 m_calibrator.recomputeParameters();
471 m_lastIterationRender = false;
472 return CONTINUE;
473 }
474 else
475 {
476 DE_ASSERT(state == gls::TheilSenCalibrator::STATE_FINISHED);
477 GLU_EXPECT_NO_ERROR(m_context.getRenderContext().getFunctions().getError(), "finish");
478 logResults();
479 return STOP;
480 }
481 }
482
483 // Texture upload tests
484
TextureUploadTests(Context & context)485 TextureUploadTests::TextureUploadTests(Context &context) : TestCaseGroup(context, "upload", "Texture upload tests")
486 {
487 }
488
~TextureUploadTests(void)489 TextureUploadTests::~TextureUploadTests(void)
490 {
491 TextureUploadTests::deinit();
492 }
493
deinit(void)494 void TextureUploadTests::deinit(void)
495 {
496 }
497
init(void)498 void TextureUploadTests::init(void)
499 {
500 TestCaseGroup *uploadCall = new TestCaseGroup(m_context, "upload", "Texture upload");
501 TestCaseGroup *uploadAndDraw =
502 new TestCaseGroup(m_context, "upload_draw_swap", "Texture upload, draw & buffer swap");
503
504 addChild(uploadCall);
505 addChild(uploadAndDraw);
506
507 static const struct
508 {
509 const char *name;
510 const char *nameLower;
511 UploadFunction func;
512 } uploadFunctions[] = {{"texImage2D", "teximage2d", UPLOAD_TEXIMAGE2D},
513 {"texSubImage2D", "texsubimage2d", UPLOAD_TEXSUBIMAGE2D}};
514
515 static const struct
516 {
517 const char *name;
518 uint32_t format;
519 uint32_t type;
520 } textureCombinations[] = {
521 {"rgb_ubyte", GL_RGB, GL_UNSIGNED_BYTE},
522 {"rgba_ubyte", GL_RGBA, GL_UNSIGNED_BYTE},
523 {"alpha_ubyte", GL_ALPHA, GL_UNSIGNED_BYTE},
524 {"luminance_ubyte", GL_LUMINANCE, GL_UNSIGNED_BYTE},
525 {"luminance-alpha_ubyte", GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE},
526 {"rgb_ushort565", GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
527 {"rgba_ushort4444", GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4},
528 {"rgba_ushort5551", GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1},
529 };
530
531 static const struct
532 {
533 int size;
534 TestCaseGroup *uploadCallGroup;
535 TestCaseGroup *uploadAndDrawGroup;
536 } textureSizes[] = {
537 {16, new TestCaseGroup(m_context, "16x16", "Texture size 16x16"),
538 new TestCaseGroup(m_context, "16x16", "Texture size 16x16")},
539 {256, new TestCaseGroup(m_context, "256x256", "Texture size 256x256"),
540 new TestCaseGroup(m_context, "256x256", "Texture size 256x256")},
541 {257, new TestCaseGroup(m_context, "257x257", "Texture size 257x257"),
542 new TestCaseGroup(m_context, "257x257", "Texture size 257x257")},
543 {1024, new TestCaseGroup(m_context, "1024x1024", "Texture size 1024x1024"),
544 new TestCaseGroup(m_context, "1024x1024", "Texture size 1024x1024")},
545 {2048, new TestCaseGroup(m_context, "2048x2048", "Texture size 2048x2048"),
546 new TestCaseGroup(m_context, "2048x2048", "Texture size 2048x2048")},
547 };
548
549 #define FOR_EACH(ITERATOR, ARRAY, BODY) \
550 for (int ITERATOR = 0; ITERATOR < DE_LENGTH_OF_ARRAY(ARRAY); ITERATOR++) \
551 BODY
552
553 FOR_EACH(uploadFunc, uploadFunctions,
554 FOR_EACH(texSize, textureSizes, FOR_EACH(texCombination, textureCombinations, {
555 string caseName = string("") + uploadFunctions[uploadFunc].nameLower + "_" +
556 textureCombinations[texCombination].name;
557 UploadFunction function = uploadFunctions[uploadFunc].func;
558 uint32_t format = textureCombinations[texCombination].format;
559 uint32_t type = textureCombinations[texCombination].type;
560 int size = textureSizes[texSize].size;
561
562 textureSizes[texSize].uploadCallGroup->addChild(
563 new TextureUploadCallCase(m_context, caseName.c_str(), "", function, format, type, size));
564 textureSizes[texSize].uploadAndDrawGroup->addChild(new TextureUploadAndDrawCase(
565 m_context, caseName.c_str(), "", function, format, type, size));
566 })))
567
568 for (int i = 0; i < DE_LENGTH_OF_ARRAY(textureSizes); i++)
569 {
570 uploadCall->addChild(textureSizes[i].uploadCallGroup);
571 uploadAndDraw->addChild(textureSizes[i].uploadAndDrawGroup);
572 }
573 }
574
575 } // namespace Performance
576 } // namespace gles2
577 } // namespace deqp
578