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/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 
     35 using namespace ::android;
     36 
     37 static uint32_t g_SleepBetweenSamplesMs = 0;
     38 static bool     g_PresentToWindow       = false;
     39 static size_t   g_BenchmarkNameLen      = 0;
     40 
     41 struct 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 
     56 static 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 
    206 static 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 
    282 class Layer {
    283 
    284 public:
    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 
    360 private:
    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 
    375 class BenchmarkRunner {
    376 
    377 public:
    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 
    509 private:
    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 
    585 static 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.
    595 static 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 
    678 done:
    679 
    680     printf("\n");
    681     fflush(stdout);
    682     r.tearDown();
    683 
    684     return success;
    685 }
    686 
    687 static 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!
    698 static 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.
    713 static 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.
    726 static 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 
    735 int 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