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