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 { "4:3 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 { "4:3 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 { "4:3 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 | ", static_cast<int>(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", 694 static_cast<int>(leftPad), "", 695 "Scenario", static_cast<int>(rightPad), ""); 696 } 697 698 // Run ALL the benchmarks! 699 static bool runTests() { 700 printResultsTableHeader(); 701 702 for (size_t i = 0; i < NELEMS(benchmarks); i++) { 703 const BenchmarkDesc& b = benchmarks[i]; 704 for (size_t j = 0; j < MAX_TEST_RUNS && b.runHeights[j]; j++) { 705 if (!runTest(b, j)) { 706 return false; 707 } 708 } 709 } 710 return true; 711 } 712 713 // Return the length longest benchmark name. 714 static size_t maxBenchmarkNameLen() { 715 size_t maxLen = 0; 716 for (size_t i = 0; i < NELEMS(benchmarks); i++) { 717 const BenchmarkDesc& b = benchmarks[i]; 718 size_t len = strlen(b.name); 719 if (len > maxLen) { 720 maxLen = len; 721 } 722 } 723 return maxLen; 724 } 725 726 // Print the command usage help to stderr. 727 static void showHelp(const char *cmd) { 728 fprintf(stderr, "usage: %s [options]\n", cmd); 729 fprintf(stderr, "options include:\n" 730 " -s N sleep for N ms between samples\n" 731 " -d display the test frame to a window\n" 732 " --help print this helpful message and exit\n" 733 ); 734 } 735 736 int main(int argc, char** argv) { 737 if (argc == 2 && 0 == strcmp(argv[1], "--help")) { 738 showHelp(argv[0]); 739 exit(0); 740 } 741 742 for (;;) { 743 int ret; 744 int option_index = 0; 745 static struct option long_options[] = { 746 {"help", no_argument, 0, 0 }, 747 { 0, 0, 0, 0 } 748 }; 749 750 ret = getopt_long(argc, argv, "ds:", 751 long_options, &option_index); 752 753 if (ret < 0) { 754 break; 755 } 756 757 switch(ret) { 758 case 'd': 759 g_PresentToWindow = true; 760 break; 761 762 case 's': 763 g_SleepBetweenSamplesMs = atoi(optarg); 764 break; 765 766 case 0: 767 if (strcmp(long_options[option_index].name, "help")) { 768 showHelp(argv[0]); 769 exit(0); 770 } 771 break; 772 773 default: 774 showHelp(argv[0]); 775 exit(2); 776 } 777 } 778 779 g_BenchmarkNameLen = maxBenchmarkNameLen(); 780 781 printf(" cmdline:"); 782 for (int i = 0; i < argc; i++) { 783 printf(" %s", argv[i]); 784 } 785 printf("\n"); 786 787 if (!runTests()) { 788 fprintf(stderr, "exiting due to error.\n"); 789 return 1; 790 } 791 } 792