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