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 mGLHelper(NULL), 288 mSurface(EGL_NO_SURFACE) { 289 } 290 291 bool setUp(const LayerDesc& desc, GLHelper* helper) { 292 bool result; 293 294 mDesc = desc; 295 mGLHelper = helper; 296 297 result = mGLHelper->createSurfaceTexture(mDesc.width, mDesc.height, 298 &mGLConsumer, &mSurface, &mTexName); 299 if (!result) { 300 return false; 301 } 302 303 mRenderer = desc.rendererFactory(); 304 result = mRenderer->setUp(helper); 305 if (!result) { 306 return false; 307 } 308 309 mComposer = desc.composerFactory(); 310 result = mComposer->setUp(desc, helper); 311 if (!result) { 312 return false; 313 } 314 315 return true; 316 } 317 318 void tearDown() { 319 if (mComposer != NULL) { 320 mComposer->tearDown(); 321 delete mComposer; 322 mComposer = NULL; 323 } 324 325 if (mRenderer != NULL) { 326 mRenderer->tearDown(); 327 delete mRenderer; 328 mRenderer = NULL; 329 } 330 331 if (mSurface != EGL_NO_SURFACE) { 332 mGLHelper->destroySurface(&mSurface); 333 mGLConsumer->abandon(); 334 } 335 mGLHelper = NULL; 336 mGLConsumer.clear(); 337 } 338 339 bool render() { 340 return mRenderer->render(mSurface); 341 } 342 343 bool prepareComposition() { 344 status_t err; 345 346 err = mGLConsumer->updateTexImage(); 347 if (err < 0) { 348 fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err); 349 return false; 350 } 351 352 return true; 353 } 354 355 bool compose() { 356 return mComposer->compose(mTexName, mGLConsumer); 357 } 358 359private: 360 LayerDesc mDesc; 361 362 GLHelper* mGLHelper; 363 364 GLuint mTexName; 365 sp<GLConsumer> mGLConsumer; 366 EGLSurface mSurface; 367 368 Renderer* mRenderer; 369 Composer* mComposer; 370}; 371 372class BenchmarkRunner { 373 374public: 375 376 BenchmarkRunner(const BenchmarkDesc& desc, size_t instance) : 377 mDesc(desc), 378 mInstance(instance), 379 mNumLayers(countLayers(desc)), 380 mGLHelper(NULL), 381 mSurface(EGL_NO_SURFACE), 382 mWindowSurface(EGL_NO_SURFACE) { 383 } 384 385 bool setUp() { 386 ATRACE_CALL(); 387 388 bool result; 389 390 float scaleFactor = float(mDesc.runHeights[mInstance]) / 391 float(mDesc.height); 392 uint32_t w = uint32_t(scaleFactor * float(mDesc.width)); 393 uint32_t h = mDesc.runHeights[mInstance]; 394 395 mGLHelper = new GLHelper(); 396 result = mGLHelper->setUp(shaders, NELEMS(shaders)); 397 if (!result) { 398 return false; 399 } 400 401 GLuint texName; 402 result = mGLHelper->createSurfaceTexture(w, h, &mGLConsumer, &mSurface, 403 &texName); 404 if (!result) { 405 return false; 406 } 407 408 for (size_t i = 0; i < mNumLayers; i++) { 409 // Scale the layer to match the current screen size. 410 LayerDesc ld = mDesc.layers[i]; 411 ld.x = int32_t(scaleFactor * float(ld.x)); 412 ld.y = int32_t(scaleFactor * float(ld.y)); 413 ld.width = uint32_t(scaleFactor * float(ld.width)); 414 ld.height = uint32_t(scaleFactor * float(ld.height)); 415 416 // Set up the layer. 417 result = mLayers[i].setUp(ld, mGLHelper); 418 if (!result) { 419 return false; 420 } 421 } 422 423 if (g_PresentToWindow) { 424 result = mGLHelper->createWindowSurface(w, h, &mSurfaceControl, 425 &mWindowSurface); 426 if (!result) { 427 return false; 428 } 429 430 result = doFrame(mWindowSurface); 431 if (!result) { 432 return false; 433 } 434 } 435 436 return true; 437 } 438 439 void tearDown() { 440 ATRACE_CALL(); 441 442 for (size_t i = 0; i < mNumLayers; i++) { 443 mLayers[i].tearDown(); 444 } 445 446 if (mGLHelper != NULL) { 447 if (mWindowSurface != EGL_NO_SURFACE) { 448 mGLHelper->destroySurface(&mWindowSurface); 449 } 450 mGLHelper->destroySurface(&mSurface); 451 mGLConsumer->abandon(); 452 mGLConsumer.clear(); 453 mSurfaceControl.clear(); 454 mGLHelper->tearDown(); 455 delete mGLHelper; 456 mGLHelper = NULL; 457 } 458 } 459 460 nsecs_t run(uint32_t warmUpFrames, uint32_t totalFrames) { 461 ATRACE_CALL(); 462 463 bool result; 464 465 resetColorGenerator(); 466 467 // Do the warm-up frames. 468 for (uint32_t i = 0; i < warmUpFrames; i++) { 469 result = doFrame(mSurface); 470 if (!result) { 471 return -1; 472 } 473 } 474 475 // Grab the fence for the start timestamp. 476 sp<Fence> startFence = mGLConsumer->getCurrentFence(); 477 478 // the timed frames. 479 for (uint32_t i = warmUpFrames; i < totalFrames; i++) { 480 result = doFrame(mSurface); 481 if (!result) { 482 return -1; 483 } 484 } 485 486 // Grab the fence for the end timestamp. 487 sp<Fence> endFence = mGLConsumer->getCurrentFence(); 488 489 // Keep doing frames until the end fence has signaled. 490 while (endFence->wait(0) == -ETIME) { 491 result = doFrame(mSurface); 492 if (!result) { 493 return -1; 494 } 495 } 496 497 // Compute the time delta. 498 nsecs_t startTime = startFence->getSignalTime(); 499 nsecs_t endTime = endFence->getSignalTime(); 500 501 return endTime - startTime; 502 } 503 504private: 505 506 bool doFrame(EGLSurface surface) { 507 bool result; 508 status_t err; 509 510 for (size_t i = 0; i < mNumLayers; i++) { 511 result = mLayers[i].render(); 512 if (!result) { 513 return false; 514 } 515 } 516 517 for (size_t i = 0; i < mNumLayers; i++) { 518 result = mLayers[i].prepareComposition(); 519 if (!result) { 520 return false; 521 } 522 } 523 524 result = mGLHelper->makeCurrent(surface); 525 if (!result) { 526 return false; 527 } 528 529 glClearColor(1.0f, 0.0f, 0.0f, 0.0f); 530 glClear(GL_COLOR_BUFFER_BIT); 531 532 for (size_t i = 0; i < mNumLayers; i++) { 533 result = mLayers[i].compose(); 534 if (!result) { 535 return false; 536 } 537 } 538 539 result = mGLHelper->swapBuffers(surface); 540 if (!result) { 541 return false; 542 } 543 544 err = mGLConsumer->updateTexImage(); 545 if (err < 0) { 546 fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err); 547 return false; 548 } 549 550 return true; 551 } 552 553 static size_t countLayers(const BenchmarkDesc& desc) { 554 size_t i; 555 for (i = 0; i < MAX_NUM_LAYERS; i++) { 556 if (desc.layers[i].rendererFactory == NULL) { 557 break; 558 } 559 } 560 return i; 561 } 562 563 const BenchmarkDesc& mDesc; 564 const size_t mInstance; 565 const size_t mNumLayers; 566 567 GLHelper* mGLHelper; 568 569 // The surface into which layers are composited 570 sp<GLConsumer> mGLConsumer; 571 EGLSurface mSurface; 572 573 // Used for displaying the surface to a window. 574 EGLSurface mWindowSurface; 575 sp<SurfaceControl> mSurfaceControl; 576 577 Layer mLayers[MAX_NUM_LAYERS]; 578}; 579 580static int cmpDouble(const double* lhs, const double* rhs) { 581 if (*lhs < *rhs) { 582 return -1; 583 } else if (*rhs < *lhs) { 584 return 1; 585 } 586 return 0; 587} 588 589// Run a single benchmark and print the result. 590static bool runTest(const BenchmarkDesc b, size_t run) { 591 bool success = true; 592 double prevResult = 0.0, result = 0.0; 593 Vector<double> samples; 594 595 uint32_t runHeight = b.runHeights[run]; 596 uint32_t runWidth = b.width * runHeight / b.height; 597 printf(" %-*s | %4d x %4d | ", static_cast<int>(g_BenchmarkNameLen), b.name, 598 runWidth, runHeight); 599 fflush(stdout); 600 601 BenchmarkRunner r(b, run); 602 if (!r.setUp()) { 603 fprintf(stderr, "error initializing runner.\n"); 604 return false; 605 } 606 607 // The slowest 1/outlierFraction sample results are ignored as potential 608 // outliers. 609 const uint32_t outlierFraction = 16; 610 const double threshold = .0025; 611 612 uint32_t warmUpFrames = 1; 613 uint32_t totalFrames = 5; 614 615 // Find the number of frames needed to run for over 100ms. 616 double runTime = 0.0; 617 while (true) { 618 runTime = double(r.run(warmUpFrames, totalFrames)); 619 if (runTime < 50e6) { 620 warmUpFrames *= 2; 621 totalFrames *= 2; 622 } else { 623 break; 624 } 625 } 626 627 628 if (totalFrames - warmUpFrames > 16) { 629 // The test runs too fast to get a stable result. Skip it. 630 printf(" fast"); 631 goto done; 632 } else if (totalFrames == 5 && runTime > 200e6) { 633 // The test runs too slow to be very useful. Skip it. 634 printf(" slow"); 635 goto done; 636 } 637 638 do { 639 size_t newSamples = samples.size(); 640 if (newSamples == 0) { 641 newSamples = 4*outlierFraction; 642 } 643 644 if (newSamples > 512) { 645 printf("varies"); 646 goto done; 647 } 648 649 for (size_t i = 0; i < newSamples; i++) { 650 double sample = double(r.run(warmUpFrames, totalFrames)); 651 652 if (g_SleepBetweenSamplesMs > 0) { 653 usleep(g_SleepBetweenSamplesMs * 1000); 654 } 655 656 if (sample < 0.0) { 657 success = false; 658 goto done; 659 } 660 661 samples.add(sample); 662 } 663 664 samples.sort(cmpDouble); 665 666 prevResult = result; 667 size_t elem = (samples.size() * (outlierFraction-1) / outlierFraction); 668 result = (samples[elem-1] + samples[elem]) * 0.5; 669 } while (fabs(result - prevResult) > threshold * result); 670 671 printf("%6.3f", result / double(totalFrames - warmUpFrames) / 1e6); 672 673done: 674 675 printf("\n"); 676 fflush(stdout); 677 r.tearDown(); 678 679 return success; 680} 681 682static void printResultsTableHeader() { 683 const char* scenario = "Scenario"; 684 size_t len = strlen(scenario); 685 size_t leftPad = (g_BenchmarkNameLen - len) / 2; 686 size_t rightPad = g_BenchmarkNameLen - len - leftPad; 687 printf(" %*s%s%*s | Resolution | Time (ms)\n", 688 static_cast<int>(leftPad), "", 689 "Scenario", static_cast<int>(rightPad), ""); 690} 691 692// Run ALL the benchmarks! 693static bool runTests() { 694 printResultsTableHeader(); 695 696 for (size_t i = 0; i < NELEMS(benchmarks); i++) { 697 const BenchmarkDesc& b = benchmarks[i]; 698 for (size_t j = 0; j < MAX_TEST_RUNS && b.runHeights[j]; j++) { 699 if (!runTest(b, j)) { 700 return false; 701 } 702 } 703 } 704 return true; 705} 706 707// Return the length longest benchmark name. 708static size_t maxBenchmarkNameLen() { 709 size_t maxLen = 0; 710 for (size_t i = 0; i < NELEMS(benchmarks); i++) { 711 const BenchmarkDesc& b = benchmarks[i]; 712 size_t len = strlen(b.name); 713 if (len > maxLen) { 714 maxLen = len; 715 } 716 } 717 return maxLen; 718} 719 720// Print the command usage help to stderr. 721static void showHelp(const char *cmd) { 722 fprintf(stderr, "usage: %s [options]\n", cmd); 723 fprintf(stderr, "options include:\n" 724 " -s N sleep for N ms between samples\n" 725 " -d display the test frame to a window\n" 726 " --help print this helpful message and exit\n" 727 ); 728} 729 730int main(int argc, char** argv) { 731 if (argc == 2 && 0 == strcmp(argv[1], "--help")) { 732 showHelp(argv[0]); 733 exit(0); 734 } 735 736 for (;;) { 737 int ret; 738 int option_index = 0; 739 static struct option long_options[] = { 740 {"help", no_argument, 0, 0 }, 741 { 0, 0, 0, 0 } 742 }; 743 744 ret = getopt_long(argc, argv, "ds:", 745 long_options, &option_index); 746 747 if (ret < 0) { 748 break; 749 } 750 751 switch(ret) { 752 case 'd': 753 g_PresentToWindow = true; 754 break; 755 756 case 's': 757 g_SleepBetweenSamplesMs = atoi(optarg); 758 break; 759 760 case 0: 761 if (strcmp(long_options[option_index].name, "help")) { 762 showHelp(argv[0]); 763 exit(0); 764 } 765 break; 766 767 default: 768 showHelp(argv[0]); 769 exit(2); 770 } 771 } 772 773 g_BenchmarkNameLen = maxBenchmarkNameLen(); 774 775 printf(" cmdline:"); 776 for (int i = 0; i < argc; i++) { 777 printf(" %s", argv[i]); 778 } 779 printf("\n"); 780 781 if (!runTests()) { 782 fprintf(stderr, "exiting due to error.\n"); 783 return 1; 784 } 785} 786