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