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         mGLHelper(nullptr),
288         mSurface(EGL_NO_SURFACE) {
289     }
290 
setUp(const LayerDesc & desc,GLHelper * helper)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 
tearDown()318     void tearDown() {
319         if (mComposer != nullptr) {
320             mComposer->tearDown();
321             delete mComposer;
322             mComposer = nullptr;
323         }
324 
325         if (mRenderer != nullptr) {
326             mRenderer->tearDown();
327             delete mRenderer;
328             mRenderer = nullptr;
329         }
330 
331         if (mSurface != EGL_NO_SURFACE) {
332             mGLHelper->destroySurface(&mSurface);
333             mGLConsumer->abandon();
334         }
335         mGLHelper = nullptr;
336         mGLConsumer.clear();
337     }
338 
render()339     bool render() {
340         return mRenderer->render(mSurface);
341     }
342 
prepareComposition()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 
compose()355     bool compose() {
356         return mComposer->compose(mTexName, mGLConsumer);
357     }
358 
359 private:
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 
372 class BenchmarkRunner {
373 
374 public:
375 
BenchmarkRunner(const BenchmarkDesc & desc,size_t instance)376     BenchmarkRunner(const BenchmarkDesc& desc, size_t instance) :
377         mDesc(desc),
378         mInstance(instance),
379         mNumLayers(countLayers(desc)),
380         mGLHelper(nullptr),
381         mSurface(EGL_NO_SURFACE),
382         mWindowSurface(EGL_NO_SURFACE) {
383     }
384 
setUp()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 
tearDown()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 != nullptr) {
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 = nullptr;
457         }
458     }
459 
run(uint32_t warmUpFrames,uint32_t totalFrames)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 
504 private:
505 
doFrame(EGLSurface surface)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 
countLayers(const BenchmarkDesc & desc)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 == nullptr) {
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 
cmpDouble(const double * lhs,const double * rhs)580 static 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.
runTest(const BenchmarkDesc b,size_t run)590 static 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 
673 done:
674 
675     printf("\n");
676     fflush(stdout);
677     r.tearDown();
678 
679     return success;
680 }
681 
printResultsTableHeader()682 static 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!
runTests()693 static 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.
maxBenchmarkNameLen()708 static 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.
showHelp(const char * cmd)721 static 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 
main(int argc,char ** argv)730 int 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