es2fFlushFinishTests.cpp revision 3c827367444ee418f129b2c238299f49d3264554
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 Flush and finish tests. 22 *//*--------------------------------------------------------------------*/ 23 24#include "es2fFlushFinishTests.hpp" 25 26#include "gluRenderContext.hpp" 27#include "gluObjectWrapper.hpp" 28#include "gluShaderProgram.hpp" 29#include "gluDrawUtil.hpp" 30 31#include "glsCalibration.hpp" 32 33#include "tcuTestLog.hpp" 34#include "tcuRenderTarget.hpp" 35 36#include "glwEnums.hpp" 37#include "glwFunctions.hpp" 38 39#include "deRandom.hpp" 40#include "deStringUtil.hpp" 41#include "deClock.h" 42#include "deThread.h" 43#include "deMath.h" 44 45#include <algorithm> 46 47namespace deqp 48{ 49namespace gles2 50{ 51namespace Functional 52{ 53 54using std::vector; 55using std::string; 56using tcu::TestLog; 57using tcu::Vec2; 58using deqp::gls::theilSenEstimator; 59using deqp::gls::LineParameters; 60 61namespace 62{ 63 64enum 65{ 66 MAX_VIEWPORT_SIZE = 128, 67 MAX_SAMPLE_DURATION_US = 1000*1000, 68 WAIT_TIME_MS = 1200, 69 NUM_SAMPLES = 25, 70 MIN_DRAW_CALL_COUNT = 10, 71 MAX_DRAW_CALL_COUNT = 1<<20, 72 NUM_ITERS_IN_SHADER = 10 73}; 74 75const float NO_CORR_COEF_THRESHOLD = 0.1f; 76const float FLUSH_COEF_THRESHOLD = 0.2f; 77const float CORRELATED_COEF_THRESHOLD = 0.5f; 78 79static void busyWait (int milliseconds) 80{ 81 const deUint64 startTime = deGetMicroseconds(); 82 float v = 2.0f; 83 84 for (;;) 85 { 86 for (int i = 0; i < 10; i++) 87 v = deFloatSin(v); 88 89 if (deGetMicroseconds()-startTime >= deUint64(1000*milliseconds)) 90 break; 91 } 92} 93 94class CalibrationFailedException : public std::runtime_error 95{ 96public: 97 CalibrationFailedException (const std::string& reason) : std::runtime_error(reason) {} 98}; 99 100class FlushFinishCase : public TestCase 101{ 102public: 103 enum ExpectedBehavior 104 { 105 EXPECT_COEF_LESS_THAN = 0, 106 EXPECT_COEF_GREATER_THAN, 107 }; 108 109 FlushFinishCase (Context& context, 110 const char* name, 111 const char* description, 112 ExpectedBehavior waitBehavior, 113 float waitThreshold, 114 ExpectedBehavior readBehavior, 115 float readThreshold); 116 ~FlushFinishCase (void); 117 118 void init (void); 119 void deinit (void); 120 IterateResult iterate (void); 121 122 struct Sample 123 { 124 int numDrawCalls; 125 deUint64 waitTime; 126 deUint64 readPixelsTime; 127 }; 128 129 struct CalibrationParams 130 { 131 int maxDrawCalls; 132 }; 133 134protected: 135 virtual void waitForGL (void) = 0; 136 137private: 138 FlushFinishCase (const FlushFinishCase&); 139 FlushFinishCase& operator= (const FlushFinishCase&); 140 141 CalibrationParams calibrate (void); 142 void analyzeResults (const std::vector<Sample>& samples, const CalibrationParams& calibrationParams); 143 144 void setupRenderState (void); 145 void render (int numDrawCalls); 146 void readPixels (void); 147 148 const ExpectedBehavior m_waitBehavior; 149 const float m_waitThreshold; 150 const ExpectedBehavior m_readBehavior; 151 const float m_readThreshold; 152 153 glu::ShaderProgram* m_program; 154}; 155 156FlushFinishCase::FlushFinishCase (Context& context, const char* name, const char* description, ExpectedBehavior waitBehavior, float waitThreshold, ExpectedBehavior readBehavior, float readThreshold) 157 : TestCase (context, name, description) 158 , m_waitBehavior (waitBehavior) 159 , m_waitThreshold (waitThreshold) 160 , m_readBehavior (readBehavior) 161 , m_readThreshold (readThreshold) 162 , m_program (DE_NULL) 163{ 164} 165 166FlushFinishCase::~FlushFinishCase (void) 167{ 168 FlushFinishCase::deinit(); 169} 170 171void FlushFinishCase::init (void) 172{ 173 DE_ASSERT(!m_program); 174 175 m_program = new glu::ShaderProgram(m_context.getRenderContext(), 176 glu::ProgramSources() 177 << glu::VertexSource( 178 "attribute highp vec4 a_position;\n" 179 "varying highp vec4 v_coord;\n" 180 "void main (void)\n" 181 "{\n" 182 " gl_Position = a_position;\n" 183 " v_coord = a_position;\n" 184 "}\n") 185 << glu::FragmentSource( 186 "uniform mediump int u_numIters;\n" 187 "varying mediump vec4 v_coord;\n" 188 "void main (void)\n" 189 "{\n" 190 " highp vec4 color = v_coord;\n" 191 " for (int i = 0; i < " + de::toString(int(NUM_ITERS_IN_SHADER)) + "; i++)\n" 192 " color = sin(color);\n" 193 " gl_FragColor = color;\n" 194 "}\n")); 195 196 if (!m_program->isOk()) 197 { 198 m_testCtx.getLog() << *m_program; 199 delete m_program; 200 m_program = DE_NULL; 201 TCU_FAIL("Compile failed"); 202 } 203} 204 205void FlushFinishCase::deinit (void) 206{ 207 delete m_program; 208 m_program = DE_NULL; 209} 210 211tcu::TestLog& operator<< (tcu::TestLog& log, const FlushFinishCase::Sample& sample) 212{ 213 log << TestLog::Message << sample.numDrawCalls << " calls:\t" << sample.waitTime << " us wait,\t" << sample.readPixelsTime << " us read" << TestLog::EndMessage; 214 return log; 215} 216 217void FlushFinishCase::setupRenderState (void) 218{ 219 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 220 const int posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position"); 221 const int viewportW = de::min<int>(m_context.getRenderTarget().getWidth(), MAX_VIEWPORT_SIZE); 222 const int viewportH = de::min<int>(m_context.getRenderTarget().getHeight(), MAX_VIEWPORT_SIZE); 223 224 static const float s_positions[] = 225 { 226 -1.0f, -1.0f, 227 +1.0f, -1.0f, 228 -1.0f, +1.0f, 229 +1.0f, +1.0f 230 }; 231 232 TCU_CHECK(posLoc >= 0); 233 234 gl.viewport(0, 0, viewportW, viewportH); 235 gl.useProgram(m_program->getProgram()); 236 gl.enableVertexAttribArray(posLoc); 237 gl.vertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, &s_positions[0]); 238 gl.enable(GL_BLEND); 239 gl.blendFunc(GL_ONE, GL_ONE); 240 gl.blendEquation(GL_FUNC_ADD); 241 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set up render state"); 242} 243 244void FlushFinishCase::render (int numDrawCalls) 245{ 246 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 247 248 const deUint8 indices[] = { 0, 1, 2, 2, 1, 3 }; 249 250 gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 251 252 for (int ndx = 0; ndx < numDrawCalls; ndx++) 253 gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_BYTE, &indices[0]); 254} 255 256void FlushFinishCase::readPixels (void) 257{ 258 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 259 deUint8 tmp[4]; 260 261 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &tmp); 262} 263 264FlushFinishCase::CalibrationParams FlushFinishCase::calibrate (void) 265{ 266 tcu::ScopedLogSection section (m_testCtx.getLog(), "CalibrationInfo", "Calibration info"); 267 CalibrationParams params; 268 269 // Find draw call count that results in desired maximum time. 270 { 271 deUint64 prevDuration = 0; 272 int prevDrawCount = 1; 273 int curDrawCount = 1; 274 275 m_testCtx.getLog() << TestLog::Message << "Calibrating maximum draw call count, target duration = " << int(MAX_SAMPLE_DURATION_US) << " us" << TestLog::EndMessage; 276 277 for (;;) 278 { 279 deUint64 curDuration; 280 281 { 282 const deUint64 startTime = deGetMicroseconds(); 283 render(curDrawCount); 284 readPixels(); 285 curDuration = deGetMicroseconds()-startTime; 286 } 287 288 m_testCtx.getLog() << TestLog::Message << "Duration with " << curDrawCount << " draw calls = " << curDuration << " us" << TestLog::EndMessage; 289 290 if (curDuration > MAX_SAMPLE_DURATION_US) 291 { 292 if (curDrawCount > 1) 293 { 294 // Compute final count by using linear estimation. 295 const float a = float(curDuration - prevDuration) / float(curDrawCount - prevDrawCount); 296 const float b = float(prevDuration) - a*float(prevDrawCount); 297 const float est = (float(MAX_SAMPLE_DURATION_US) - b) / a; 298 299 curDrawCount = de::clamp(deFloorFloatToInt32(est), 1, int(MAX_DRAW_CALL_COUNT)); 300 } 301 // else: Settle on 1. 302 303 break; 304 } 305 else if (curDrawCount >= MAX_DRAW_CALL_COUNT) 306 break; // Settle on maximum. 307 else 308 { 309 prevDrawCount = curDrawCount; 310 prevDuration = curDuration; 311 curDrawCount = curDrawCount*2; 312 } 313 } 314 315 params.maxDrawCalls = curDrawCount; 316 317 m_testCtx.getLog() << TestLog::Integer("MaxDrawCalls", "Maximum number of draw calls", "", QP_KEY_TAG_NONE, params.maxDrawCalls); 318 } 319 320 // Sanity check. 321 if (params.maxDrawCalls < MIN_DRAW_CALL_COUNT) 322 throw CalibrationFailedException("Calibration failed, maximum draw call count is too low"); 323 324 return params; 325} 326 327struct CompareSampleDrawCount 328{ 329 bool operator() (const FlushFinishCase::Sample& a, const FlushFinishCase::Sample& b) const { return a.numDrawCalls < b.numDrawCalls; } 330}; 331 332std::vector<Vec2> getPointsFromSamples (const std::vector<FlushFinishCase::Sample>& samples, const deUint64 FlushFinishCase::Sample::*field) 333{ 334 vector<Vec2> points(samples.size()); 335 336 for (size_t ndx = 0; ndx < samples.size(); ndx++) 337 points[ndx] = Vec2(float(samples[ndx].numDrawCalls), float(samples[ndx].*field)); 338 339 return points; 340} 341 342template<typename T> 343T getMaximumValue (const std::vector<FlushFinishCase::Sample>& samples, const T FlushFinishCase::Sample::*field) 344{ 345 DE_ASSERT(!samples.empty()); 346 347 T maxVal = samples[0].*field; 348 349 for (size_t ndx = 1; ndx < samples.size(); ndx++) 350 maxVal = de::max(maxVal, samples[ndx].*field); 351 352 return maxVal; 353} 354 355void FlushFinishCase::analyzeResults (const std::vector<Sample>& samples, const CalibrationParams& calibrationParams) 356{ 357 const vector<Vec2> waitTimes = getPointsFromSamples(samples, &Sample::waitTime); 358 const vector<Vec2> readTimes = getPointsFromSamples(samples, &Sample::readPixelsTime); 359 const LineParameters waitLine = theilSenEstimator(waitTimes); 360 const LineParameters readLine = theilSenEstimator(readTimes); 361 const float normWaitCoef = waitLine.coefficient * float(calibrationParams.maxDrawCalls) / float(MAX_SAMPLE_DURATION_US); 362 const float normReadCoef = readLine.coefficient * float(calibrationParams.maxDrawCalls) / float(MAX_SAMPLE_DURATION_US); 363 bool allOk = true; 364 365 { 366 tcu::ScopedLogSection section (m_testCtx.getLog(), "Samples", "Samples"); 367 vector<Sample> sortedSamples (samples.begin(), samples.end()); 368 369 std::sort(sortedSamples.begin(), sortedSamples.end(), CompareSampleDrawCount()); 370 371 for (vector<Sample>::const_iterator iter = sortedSamples.begin(); iter != sortedSamples.end(); ++iter) 372 m_testCtx.getLog() << *iter; 373 } 374 375 m_testCtx.getLog() << TestLog::Float("WaitCoefficient", "Wait coefficient", "", QP_KEY_TAG_NONE, waitLine.coefficient) 376 << TestLog::Float("ReadCoefficient", "Read coefficient", "", QP_KEY_TAG_NONE, readLine.coefficient) 377 << TestLog::Float("NormalizedWaitCoefficient", "Normalized wait coefficient", "", QP_KEY_TAG_NONE, normWaitCoef) 378 << TestLog::Float("NormalizedReadCoefficient", "Normalized read coefficient", "", QP_KEY_TAG_NONE, normReadCoef); 379 380 { 381 const bool waitCorrelated = normWaitCoef > CORRELATED_COEF_THRESHOLD; 382 const bool readCorrelated = normReadCoef > CORRELATED_COEF_THRESHOLD; 383 const bool waitNotCorr = normWaitCoef < NO_CORR_COEF_THRESHOLD; 384 const bool readNotCorr = normReadCoef < NO_CORR_COEF_THRESHOLD; 385 386 if (waitCorrelated || waitNotCorr) 387 m_testCtx.getLog() << TestLog::Message << "Wait time is" << (waitCorrelated ? "" : " NOT") << " correlated to rendering workload size." << TestLog::EndMessage; 388 else 389 m_testCtx.getLog() << TestLog::Message << "Warning: Wait time correlation to rendering workload size is unclear." << TestLog::EndMessage; 390 391 if (readCorrelated || readNotCorr) 392 m_testCtx.getLog() << TestLog::Message << "Read time is" << (readCorrelated ? "" : " NOT") << " correlated to rendering workload size." << TestLog::EndMessage; 393 else 394 m_testCtx.getLog() << TestLog::Message << "Warning: Read time correlation to rendering workload size is unclear." << TestLog::EndMessage; 395 } 396 397 for (int ndx = 0; ndx < 2; ndx++) 398 { 399 const float coef = ndx == 0 ? normWaitCoef : normReadCoef; 400 const char* name = ndx == 0 ? "wait" : "read"; 401 const ExpectedBehavior behavior = ndx == 0 ? m_waitBehavior : m_readBehavior; 402 const float threshold = ndx == 0 ? m_waitThreshold : m_readThreshold; 403 const bool isOk = behavior == EXPECT_COEF_GREATER_THAN ? coef > threshold : 404 behavior == EXPECT_COEF_LESS_THAN ? coef < threshold : false; 405 const char* cmpName = behavior == EXPECT_COEF_GREATER_THAN ? "greater than" : 406 behavior == EXPECT_COEF_LESS_THAN ? "less than" : DE_NULL; 407 408 if (!isOk) 409 { 410 m_testCtx.getLog() << TestLog::Message << "ERROR: Expected " << name << " coefficient to be " << cmpName << " " << threshold << TestLog::EndMessage; 411 allOk = false; 412 } 413 } 414 415 m_testCtx.setTestResult(allOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, 416 allOk ? "Pass" : "Suspicious performance behavior"); 417} 418 419FlushFinishCase::IterateResult FlushFinishCase::iterate (void) 420{ 421 vector<Sample> samples (NUM_SAMPLES); 422 CalibrationParams params; 423 424 // Try to poke CPU into full speed. \todo [2013-12-26 pyry] Use more robust method. 425 busyWait(10); 426 427 setupRenderState(); 428 429 // Do one full render cycle. 430 { 431 render(1); 432 readPixels(); 433 } 434 435 // Calibrate. 436 try 437 { 438 params = calibrate(); 439 } 440 catch (const CalibrationFailedException& e) 441 { 442 m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, e.what()); 443 return STOP; 444 } 445 446 // Do measurement. 447 { 448 de::Random rnd (123); 449 450 for (size_t ndx = 0; ndx < samples.size(); ndx++) 451 { 452 const int drawCallCount = rnd.getInt(1, params.maxDrawCalls); 453 deUint64 waitStartTime; 454 deUint64 readStartTime; 455 deUint64 readFinishTime; 456 457 render(drawCallCount); 458 459 waitStartTime = deGetMicroseconds(); 460 waitForGL(); 461 462 readStartTime = deGetMicroseconds(); 463 readPixels(); 464 readFinishTime = deGetMicroseconds(); 465 466 samples[ndx].numDrawCalls = drawCallCount; 467 samples[ndx].waitTime = readStartTime-waitStartTime; 468 samples[ndx].readPixelsTime = readFinishTime-readStartTime; 469 470 if (m_testCtx.getWatchDog()) 471 qpWatchDog_touch(m_testCtx.getWatchDog()); 472 } 473 } 474 475 // Analyze - sets test case result. 476 analyzeResults(samples, params); 477 478 return STOP; 479} 480 481class WaitOnlyCase : public FlushFinishCase 482{ 483public: 484 WaitOnlyCase (Context& context) 485 : FlushFinishCase(context, "wait", "Wait only", EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD, EXPECT_COEF_GREATER_THAN, -1000.0f /* practically nothing is expected */) 486 { 487 } 488 489 void init (void) 490 { 491 m_testCtx.getLog() << TestLog::Message << int(WAIT_TIME_MS) << " ms busy wait" << TestLog::EndMessage; 492 FlushFinishCase::init(); 493 } 494 495protected: 496 void waitForGL (void) 497 { 498 busyWait(WAIT_TIME_MS); 499 } 500}; 501 502class FlushOnlyCase : public FlushFinishCase 503{ 504public: 505 FlushOnlyCase (Context& context) 506 : FlushFinishCase(context, "flush", "Flush only", EXPECT_COEF_LESS_THAN, FLUSH_COEF_THRESHOLD, EXPECT_COEF_GREATER_THAN, CORRELATED_COEF_THRESHOLD) 507 { 508 } 509 510 void init (void) 511 { 512 m_testCtx.getLog() << TestLog::Message << "Single call to glFlush()" << TestLog::EndMessage; 513 FlushFinishCase::init(); 514 } 515 516protected: 517 void waitForGL (void) 518 { 519 m_context.getRenderContext().getFunctions().flush(); 520 } 521}; 522 523class FlushWaitCase : public FlushFinishCase 524{ 525public: 526 FlushWaitCase (Context& context) 527 : FlushFinishCase(context, "flush_wait", "Wait after flushing", EXPECT_COEF_LESS_THAN, FLUSH_COEF_THRESHOLD, EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD) 528 { 529 } 530 531 void init (void) 532 { 533 m_testCtx.getLog() << TestLog::Message << "glFlush() followed by " << int(WAIT_TIME_MS) << " ms busy wait" << TestLog::EndMessage; 534 FlushFinishCase::init(); 535 } 536 537protected: 538 void waitForGL (void) 539 { 540 m_context.getRenderContext().getFunctions().flush(); 541 busyWait(WAIT_TIME_MS); 542 } 543}; 544 545class FinishOnlyCase : public FlushFinishCase 546{ 547public: 548 FinishOnlyCase (Context& context) 549 : FlushFinishCase(context, "finish", "Finish only", EXPECT_COEF_GREATER_THAN, CORRELATED_COEF_THRESHOLD, EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD) 550 { 551 } 552 553 void init (void) 554 { 555 m_testCtx.getLog() << TestLog::Message << "Single call to glFinish()" << TestLog::EndMessage; 556 FlushFinishCase::init(); 557 } 558 559protected: 560 void waitForGL (void) 561 { 562 m_context.getRenderContext().getFunctions().finish(); 563 } 564}; 565 566class FinishWaitCase : public FlushFinishCase 567{ 568public: 569 FinishWaitCase (Context& context) 570 : FlushFinishCase(context, "finish_wait", "Finish and wait", EXPECT_COEF_GREATER_THAN, CORRELATED_COEF_THRESHOLD, EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD) 571 { 572 } 573 574 void init (void) 575 { 576 m_testCtx.getLog() << TestLog::Message << "glFinish() followed by " << int(WAIT_TIME_MS) << " ms busy wait" << TestLog::EndMessage; 577 FlushFinishCase::init(); 578 } 579 580protected: 581 void waitForGL (void) 582 { 583 m_context.getRenderContext().getFunctions().finish(); 584 busyWait(WAIT_TIME_MS); 585 } 586}; 587 588} // anonymous 589 590FlushFinishTests::FlushFinishTests (Context& context) 591 : TestCaseGroup(context, "flush_finish", "Flush and Finish tests") 592{ 593} 594 595FlushFinishTests::~FlushFinishTests (void) 596{ 597} 598 599void FlushFinishTests::init (void) 600{ 601 addChild(new WaitOnlyCase (m_context)); 602 addChild(new FlushOnlyCase (m_context)); 603 addChild(new FlushWaitCase (m_context)); 604 addChild(new FinishOnlyCase (m_context)); 605 addChild(new FinishWaitCase (m_context)); 606} 607 608} // Functional 609} // gles2 610} // deqp 611