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