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 
Layer()286     Layer() :
287         mFirstFrame(true),
288         mGLHelper(NULL),
289         mSurface(EGL_NO_SURFACE) {
290     }
291 
setUp(const LayerDesc & desc,GLHelper * helper)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 
tearDown()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 
render()340     bool render() {
341         return mRenderer->render(mSurface);
342     }
343 
prepareComposition()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 
compose()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 
BenchmarkRunner(const BenchmarkDesc & desc,size_t instance)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 
setUp()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 
tearDown()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 
run(uint32_t warmUpFrames,uint32_t totalFrames)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 
doFrame(EGLSurface surface)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 
countLayers(const BenchmarkDesc & desc)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 
cmpDouble(const double * lhs,const double * rhs)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.
runTest(const BenchmarkDesc b,size_t run)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 
printResultsTableHeader()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!
runTests()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.
maxBenchmarkNameLen()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.
showHelp(const char * cmd)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 
main(int argc,char ** argv)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