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