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