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