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