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