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