Home | History | Annotate | Download | only in flatland
      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