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