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