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