Main.cpp revision 9c183f2493222000fa512d927cfde3f4c748eda0
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define ATRACE_TAG ATRACE_TAG_ALWAYS 18 19#include <gui/GraphicBufferAlloc.h> 20#include <gui/Surface.h> 21#include <gui/GLConsumer.h> 22#include <gui/SurfaceTextureClient.h> 23#include <ui/Fence.h> 24#include <utils/Trace.h> 25 26#include <EGL/egl.h> 27#include <GLES2/gl2.h> 28 29#include <math.h> 30#include <getopt.h> 31 32#include "Flatland.h" 33#include "GLHelper.h" 34 35using namespace ::android; 36 37static uint32_t g_SleepBetweenSamplesMs = 0; 38static bool g_PresentToWindow = false; 39static size_t g_BenchmarkNameLen = 0; 40 41struct BenchmarkDesc { 42 // The name of the test. 43 const char* name; 44 45 // The dimensions of the space in which window layers are specified. 46 uint32_t width; 47 uint32_t height; 48 49 // The screen heights at which to run the test. 50 uint32_t runHeights[MAX_TEST_RUNS]; 51 52 // The list of window layers. 53 LayerDesc layers[MAX_NUM_LAYERS]; 54}; 55 56static const BenchmarkDesc benchmarks[] = { 57 { "16:10 Single Static Window", 58 2560, 1600, { 800, 1600, 2400 }, 59 { 60 { // Window 61 0, staticGradient, opaque, 62 0, 50, 2560, 1454, 63 }, 64 { // Status bar 65 0, staticGradient, opaque, 66 0, 0, 2560, 50, 67 }, 68 { // Navigation bar 69 0, staticGradient, opaque, 70 0, 1504, 2560, 96, 71 }, 72 }, 73 }, 74 75 { "16:10 App -> Home Transition", 76 2560, 1600, { 800, 1600, 2400 }, 77 { 78 { // Wallpaper 79 0, staticGradient, opaque, 80 0, 50, 2560, 1454, 81 }, 82 { // Launcher 83 0, staticGradient, blend, 84 0, 50, 2560, 1454, 85 }, 86 { // Outgoing activity 87 0, staticGradient, blendShrink, 88 20, 70, 2520, 1414, 89 }, 90 { // Status bar 91 0, staticGradient, opaque, 92 0, 0, 2560, 50, 93 }, 94 { // Navigation bar 95 0, staticGradient, opaque, 96 0, 1504, 2560, 96, 97 }, 98 }, 99 }, 100 101 { "16:10 SurfaceView -> Home Transition", 102 2560, 1600, { 800, 1600, 2400 }, 103 { 104 { // Wallpaper 105 0, staticGradient, opaque, 106 0, 50, 2560, 1454, 107 }, 108 { // Launcher 109 0, staticGradient, blend, 110 0, 50, 2560, 1454, 111 }, 112 { // Outgoing SurfaceView 113 0, staticGradient, blendShrink, 114 20, 70, 2520, 1414, 115 }, 116 { // Outgoing activity 117 0, staticGradient, blendShrink, 118 20, 70, 2520, 1414, 119 }, 120 { // Status bar 121 0, staticGradient, opaque, 122 0, 0, 2560, 50, 123 }, 124 { // Navigation bar 125 0, staticGradient, opaque, 126 0, 1504, 2560, 96, 127 }, 128 }, 129 }, 130}; 131 132static const ShaderDesc shaders[] = { 133 { 134 name: "Blit", 135 vertexShader: { 136 "precision mediump float;", 137 "", 138 "attribute vec4 position;", 139 "attribute vec4 uv;", 140 "", 141 "varying vec4 texCoords;", 142 "", 143 "uniform mat4 objToNdc;", 144 "uniform mat4 uvToTex;", 145 "", 146 "void main() {", 147 " gl_Position = objToNdc * position;", 148 " texCoords = uvToTex * uv;", 149 "}", 150 }, 151 fragmentShader: { 152 "#extension GL_OES_EGL_image_external : require", 153 "precision mediump float;", 154 "", 155 "varying vec4 texCoords;", 156 "", 157 "uniform samplerExternalOES blitSrc;", 158 "uniform vec4 modColor;", 159 "", 160 "void main() {", 161 " gl_FragColor = texture2D(blitSrc, texCoords.xy);", 162 " gl_FragColor *= modColor;", 163 "}", 164 }, 165 }, 166 167 { 168 name: "Gradient", 169 vertexShader: { 170 "precision mediump float;", 171 "", 172 "attribute vec4 position;", 173 "attribute vec4 uv;", 174 "", 175 "varying float interp;", 176 "", 177 "uniform mat4 objToNdc;", 178 "uniform mat4 uvToInterp;", 179 "", 180 "void main() {", 181 " gl_Position = objToNdc * position;", 182 " interp = (uvToInterp * uv).x;", 183 "}", 184 }, 185 fragmentShader: { 186 "precision mediump float;", 187 "", 188 "varying float interp;", 189 "", 190 "uniform vec4 color0;", 191 "uniform vec4 color1;", 192 "", 193 "uniform sampler2D ditherKernel;", 194 "uniform float invDitherKernelSize;", 195 "uniform float invDitherKernelSizeSq;", 196 "", 197 "void main() {", 198 " float dither = texture2D(ditherKernel,", 199 " gl_FragCoord.xy * invDitherKernelSize).a;", 200 " dither *= invDitherKernelSizeSq;", 201 " vec4 color = mix(color0, color1, clamp(interp, 0.0, 1.0));", 202 " gl_FragColor = color + vec4(dither, dither, dither, 0.0);", 203 "}", 204 }, 205 }, 206}; 207 208class Layer { 209 210public: 211 212 Layer() : 213 mFirstFrame(true), 214 mGLHelper(NULL), 215 mSurface(EGL_NO_SURFACE) { 216 } 217 218 bool setUp(const LayerDesc& desc, GLHelper* helper) { 219 bool result; 220 221 mDesc = desc; 222 mGLHelper = helper; 223 224 result = mGLHelper->createSurfaceTexture(mDesc.width, mDesc.height, 225 &mGLConsumer, &mSurface, &mTexName); 226 if (!result) { 227 return false; 228 } 229 230 mRenderer = desc.rendererFactory(); 231 result = mRenderer->setUp(helper); 232 if (!result) { 233 return false; 234 } 235 236 mComposer = desc.composerFactory(); 237 result = mComposer->setUp(desc, helper); 238 if (!result) { 239 return false; 240 } 241 242 return true; 243 } 244 245 void tearDown() { 246 if (mComposer != NULL) { 247 mComposer->tearDown(); 248 delete mComposer; 249 mComposer = NULL; 250 } 251 252 if (mRenderer != NULL) { 253 mRenderer->tearDown(); 254 delete mRenderer; 255 mRenderer = NULL; 256 } 257 258 if (mSurface != EGL_NO_SURFACE) { 259 mGLHelper->destroySurface(&mSurface); 260 mGLConsumer->abandon(); 261 } 262 mGLHelper = NULL; 263 mGLConsumer.clear(); 264 } 265 266 bool render() { 267 return mRenderer->render(mSurface); 268 } 269 270 bool prepareComposition() { 271 status_t err; 272 273 err = mGLConsumer->updateTexImage(); 274 if (err < 0) { 275 fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err); 276 return false; 277 } 278 279 return true; 280 } 281 282 bool compose() { 283 return mComposer->compose(mTexName, mGLConsumer); 284 } 285 286private: 287 bool mFirstFrame; 288 289 LayerDesc mDesc; 290 291 GLHelper* mGLHelper; 292 293 GLuint mTexName; 294 sp<GLConsumer> mGLConsumer; 295 EGLSurface mSurface; 296 297 Renderer* mRenderer; 298 Composer* mComposer; 299}; 300 301class BenchmarkRunner { 302 303public: 304 305 BenchmarkRunner(const BenchmarkDesc& desc, size_t instance) : 306 mDesc(desc), 307 mInstance(instance), 308 mNumLayers(countLayers(desc)), 309 mGLHelper(NULL), 310 mSurface(EGL_NO_SURFACE), 311 mWindowSurface(EGL_NO_SURFACE) { 312 } 313 314 bool setUp() { 315 ATRACE_CALL(); 316 317 bool result; 318 EGLint resulte; 319 320 float scaleFactor = float(mDesc.runHeights[mInstance]) / 321 float(mDesc.height); 322 uint32_t w = uint32_t(scaleFactor * float(mDesc.width)); 323 uint32_t h = mDesc.runHeights[mInstance]; 324 325 mGLHelper = new GLHelper(); 326 result = mGLHelper->setUp(shaders, NELEMS(shaders)); 327 if (!result) { 328 return false; 329 } 330 331 GLuint texName; 332 result = mGLHelper->createSurfaceTexture(w, h, &mGLConsumer, &mSurface, 333 &texName); 334 if (!result) { 335 return false; 336 } 337 338 for (size_t i = 0; i < mNumLayers; i++) { 339 // Scale the layer to match the current screen size. 340 LayerDesc ld = mDesc.layers[i]; 341 ld.x = int32_t(scaleFactor * float(ld.x)); 342 ld.y = int32_t(scaleFactor * float(ld.y)); 343 ld.width = uint32_t(scaleFactor * float(ld.width)); 344 ld.height = uint32_t(scaleFactor * float(ld.height)); 345 346 // Set up the layer. 347 result = mLayers[i].setUp(ld, mGLHelper); 348 if (!result) { 349 return false; 350 } 351 } 352 353 if (g_PresentToWindow) { 354 result = mGLHelper->createWindowSurface(w, h, &mSurfaceControl, 355 &mWindowSurface); 356 if (!result) { 357 return false; 358 } 359 360 result = doFrame(mWindowSurface); 361 if (!result) { 362 return false; 363 } 364 } 365 366 return true; 367 } 368 369 void tearDown() { 370 ATRACE_CALL(); 371 372 for (size_t i = 0; i < mNumLayers; i++) { 373 mLayers[i].tearDown(); 374 } 375 376 if (mGLHelper != NULL) { 377 if (mWindowSurface != EGL_NO_SURFACE) { 378 mGLHelper->destroySurface(&mWindowSurface); 379 } 380 mGLHelper->destroySurface(&mSurface); 381 mGLConsumer->abandon(); 382 mGLConsumer.clear(); 383 mSurfaceControl.clear(); 384 mGLHelper->tearDown(); 385 delete mGLHelper; 386 mGLHelper = NULL; 387 } 388 } 389 390 nsecs_t run(uint32_t warmUpFrames, uint32_t totalFrames) { 391 ATRACE_CALL(); 392 393 bool result; 394 status_t err; 395 396 resetColorGenerator(); 397 398 // Do the warm-up frames. 399 for (uint32_t i = 0; i < warmUpFrames; i++) { 400 result = doFrame(mSurface); 401 if (!result) { 402 return -1; 403 } 404 } 405 406 // Grab the fence for the start timestamp. 407 sp<Fence> startFence = mGLConsumer->getCurrentFence(); 408 409 // the timed frames. 410 for (uint32_t i = warmUpFrames; i < totalFrames; i++) { 411 result = doFrame(mSurface); 412 if (!result) { 413 return -1; 414 } 415 } 416 417 // Grab the fence for the end timestamp. 418 sp<Fence> endFence = mGLConsumer->getCurrentFence(); 419 420 // Keep doing frames until the end fence has signaled. 421 while (endFence->wait(0) == -ETIME) { 422 result = doFrame(mSurface); 423 if (!result) { 424 return -1; 425 } 426 } 427 428 // Compute the time delta. 429 nsecs_t startTime = startFence->getSignalTime(); 430 nsecs_t endTime = endFence->getSignalTime(); 431 432 return endTime - startTime; 433 } 434 435private: 436 437 bool doFrame(EGLSurface surface) { 438 bool result; 439 status_t err; 440 441 for (size_t i = 0; i < mNumLayers; i++) { 442 result = mLayers[i].render(); 443 if (!result) { 444 return false; 445 } 446 } 447 448 for (size_t i = 0; i < mNumLayers; i++) { 449 result = mLayers[i].prepareComposition(); 450 if (!result) { 451 return false; 452 } 453 } 454 455 result = mGLHelper->makeCurrent(surface); 456 if (!result) { 457 return false; 458 } 459 460 glClearColor(1.0f, 0.0f, 0.0f, 0.0f); 461 glClear(GL_COLOR_BUFFER_BIT); 462 463 for (size_t i = 0; i < mNumLayers; i++) { 464 result = mLayers[i].compose(); 465 if (!result) { 466 return false; 467 } 468 } 469 470 result = mGLHelper->swapBuffers(surface); 471 if (!result) { 472 return false; 473 } 474 475 err = mGLConsumer->updateTexImage(); 476 if (err < 0) { 477 fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err); 478 return false; 479 } 480 481 return true; 482 } 483 484 static size_t countLayers(const BenchmarkDesc& desc) { 485 size_t i; 486 for (i = 0; i < MAX_NUM_LAYERS; i++) { 487 if (desc.layers[i].rendererFactory == NULL) { 488 break; 489 } 490 } 491 return i; 492 } 493 494 const BenchmarkDesc& mDesc; 495 const size_t mInstance; 496 const size_t mNumLayers; 497 498 GLHelper* mGLHelper; 499 500 // The surface into which layers are composited 501 sp<GLConsumer> mGLConsumer; 502 EGLSurface mSurface; 503 504 // Used for displaying the surface to a window. 505 EGLSurface mWindowSurface; 506 sp<SurfaceControl> mSurfaceControl; 507 508 Layer mLayers[MAX_NUM_LAYERS]; 509}; 510 511static int cmpDouble(const double* lhs, const double* rhs) { 512 if (*lhs < *rhs) { 513 return -1; 514 } else if (*rhs < *lhs) { 515 return 1; 516 } 517 return 0; 518} 519 520// Run a single benchmark and print the result. 521static bool runTest(const BenchmarkDesc b, size_t run) { 522 bool success = true; 523 double prevResult = 0.0, result = 0.0; 524 Vector<double> samples; 525 526 uint32_t runHeight = b.runHeights[run]; 527 uint32_t runWidth = b.width * runHeight / b.height; 528 printf(" %-*s | %4d x %4d | ", g_BenchmarkNameLen, b.name, 529 runWidth, runHeight); 530 fflush(stdout); 531 532 BenchmarkRunner r(b, run); 533 if (!r.setUp()) { 534 fprintf(stderr, "error initializing runner.\n"); 535 return false; 536 } 537 538 // The slowest 1/outlierFraction sample results are ignored as potential 539 // outliers. 540 const uint32_t outlierFraction = 16; 541 const double threshold = .0025; 542 543 uint32_t warmUpFrames = 1; 544 uint32_t totalFrames = 5; 545 546 // Find the number of frames needed to run for over 100ms. 547 double runTime = 0.0; 548 while (true) { 549 runTime = double(r.run(warmUpFrames, totalFrames)); 550 if (runTime < 50e6) { 551 warmUpFrames *= 2; 552 totalFrames *= 2; 553 } else { 554 break; 555 } 556 } 557 558 559 if (totalFrames - warmUpFrames > 16) { 560 // The test runs too fast to get a stable result. Skip it. 561 printf(" fast"); 562 goto done; 563 } else if (totalFrames == 5 && runTime > 200e6) { 564 // The test runs too slow to be very useful. Skip it. 565 printf(" slow"); 566 goto done; 567 } 568 569 do { 570 size_t newSamples = samples.size(); 571 if (newSamples == 0) { 572 newSamples = 4*outlierFraction; 573 } 574 575 if (newSamples > 512) { 576 printf("varies"); 577 goto done; 578 } 579 580 for (size_t i = 0; i < newSamples; i++) { 581 double sample = double(r.run(warmUpFrames, totalFrames)); 582 583 if (g_SleepBetweenSamplesMs > 0) { 584 usleep(g_SleepBetweenSamplesMs * 1000); 585 } 586 587 if (sample < 0.0) { 588 success = false; 589 goto done; 590 } 591 592 samples.add(sample); 593 } 594 595 samples.sort(cmpDouble); 596 597 prevResult = result; 598 size_t elem = (samples.size() * (outlierFraction-1) / outlierFraction); 599 result = (samples[elem-1] + samples[elem]) * 0.5; 600 } while (fabs(result - prevResult) > threshold * result); 601 602 printf("%6.3f", result / double(totalFrames - warmUpFrames) / 1e6); 603 604done: 605 606 printf("\n"); 607 fflush(stdout); 608 r.tearDown(); 609 610 return success; 611} 612 613static void printResultsTableHeader() { 614 const char* scenario = "Scenario"; 615 size_t len = strlen(scenario); 616 size_t leftPad = (g_BenchmarkNameLen - len) / 2; 617 size_t rightPad = g_BenchmarkNameLen - len - leftPad; 618 printf(" %*s%s%*s | Resolution | Time (ms)\n", leftPad, "", 619 "Scenario", rightPad, ""); 620} 621 622// Run ALL the benchmarks! 623static bool runTests() { 624 printResultsTableHeader(); 625 626 for (size_t i = 0; i < NELEMS(benchmarks); i++) { 627 const BenchmarkDesc& b = benchmarks[i]; 628 for (size_t j = 0; j < MAX_TEST_RUNS && b.runHeights[j]; j++) { 629 if (!runTest(b, j)) { 630 return false; 631 } 632 } 633 } 634 return true; 635} 636 637// Return the length longest benchmark name. 638static size_t maxBenchmarkNameLen() { 639 size_t maxLen = 0; 640 for (size_t i = 0; i < NELEMS(benchmarks); i++) { 641 const BenchmarkDesc& b = benchmarks[i]; 642 size_t len = strlen(b.name); 643 if (len > maxLen) { 644 maxLen = len; 645 } 646 } 647 return maxLen; 648} 649 650// Print the command usage help to stderr. 651static void showHelp(const char *cmd) { 652 fprintf(stderr, "usage: %s [options]\n", cmd); 653 fprintf(stderr, "options include:\n" 654 " -s N sleep for N ms between samples\n" 655 " -d display the test frame to a window\n" 656 " --help print this helpful message and exit\n" 657 ); 658} 659 660int main(int argc, char** argv) { 661 if (argc == 2 && 0 == strcmp(argv[1], "--help")) { 662 showHelp(argv[0]); 663 exit(0); 664 } 665 666 for (;;) { 667 int ret; 668 int option_index = 0; 669 static struct option long_options[] = { 670 {"help", no_argument, 0, 0 }, 671 { 0, 0, 0, 0 } 672 }; 673 674 ret = getopt_long(argc, argv, "ds:", 675 long_options, &option_index); 676 677 if (ret < 0) { 678 break; 679 } 680 681 switch(ret) { 682 case 'd': 683 g_PresentToWindow = true; 684 break; 685 686 case 's': 687 g_SleepBetweenSamplesMs = atoi(optarg); 688 break; 689 690 case 0: 691 if (strcmp(long_options[option_index].name, "help")) { 692 showHelp(argv[0]); 693 exit(0); 694 } 695 break; 696 697 default: 698 showHelp(argv[0]); 699 exit(2); 700 } 701 } 702 703 g_BenchmarkNameLen = maxBenchmarkNameLen(); 704 705 printf(" cmdline:"); 706 for (int i = 0; i < argc; i++) { 707 printf(" %s", argv[i]); 708 } 709 printf("\n"); 710 711 if (!runTests()) { 712 fprintf(stderr, "exiting due to error.\n"); 713 return 1; 714 } 715} 716