Add some options to macrobench

Change-Id: If8d5f5d3ace050577986a554182b2b66fd2257e1
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index eb9b55f..c305f65 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -243,6 +243,7 @@
     dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
     dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
             (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
+    dprintf(fd, "\n50th percentile: %ums", findPercentile(data, 50));
     dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
     dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
     dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 0669596..083aeb7 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -53,6 +53,8 @@
 ProfileType Properties::sProfileType = ProfileType::None;
 bool Properties::sDisableProfileBars = false;
 
+bool Properties::waitForGpuCompletion = false;
+
 static int property_get_int(const char* key, int defaultValue) {
     char buf[PROPERTY_VALUE_MAX] = {'\0',};
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1dde7e0..88f1dbc 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -285,6 +285,9 @@
 
     static ProfileType getProfileType();
 
+    // Should be used only by test apps
+    static bool waitForGpuCompletion;
+
 private:
     static ProfileType sProfileType;
     static bool sDisableProfileBars;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 78df297..466fef9d 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -29,8 +29,6 @@
 
 #define GLES_VERSION 2
 
-#define WAIT_FOR_GPU_COMPLETION 0
-
 // Android-specific addition that is used to show when frames began in systrace
 EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
 
@@ -179,7 +177,10 @@
 }
 
 void EglManager::createContext() {
-    EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
+    EGLint attribs[] = {
+            EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
+            EGL_NONE
+    };
     mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
     LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
         "Failed to create context, error = %s", egl_error_str());
@@ -318,12 +319,10 @@
 
 bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
 
-#if WAIT_FOR_GPU_COMPLETION
-    {
+    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
         ATRACE_NAME("Finishing GPU work");
         fence();
     }
-#endif
 
     EGLint rects[4];
     frame.map(screenDirty, rects);
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index df8d194..706f2ff 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -37,6 +37,7 @@
 public:
     struct Options {
         int count = 0;
+        int reportFrametimeWeight = 0;
     };
 
     template <class T>
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 8261220..a843e92 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -38,6 +38,30 @@
     }
 };
 
+template<class T>
+class ModifiedMovingAverage {
+public:
+    ModifiedMovingAverage(int weight) : mWeight(weight) {}
+
+    T add(T today) {
+        if (!mHasValue) {
+            mAverage = today;
+        } else {
+            mAverage = (((mWeight - 1) * mAverage) + today) / mWeight;
+        }
+        return mAverage;
+    }
+
+    T average() {
+        return mAverage;
+    }
+
+private:
+    bool mHasValue = false;
+    int mWeight;
+    T mAverage;
+};
+
 void run(const TestScene::Info& info, const TestScene::Options& opts) {
     // Switch to the real display
     gDisplay = getBuiltInDisplay();
@@ -67,22 +91,35 @@
     proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
 
     // Do a few cold runs then reset the stats so that the caches are all hot
-    for (int i = 0; i < 3; i++) {
+    for (int i = 0; i < 5; i++) {
         testContext.waitForVsync();
         nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
         UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
         proxy->syncAndDrawFrame();
     }
+
     proxy->resetProfileInfo();
+    proxy->fence();
+
+    ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
 
     for (int i = 0; i < opts.count; i++) {
         testContext.waitForVsync();
-
-        ATRACE_NAME("UI-Draw Frame");
         nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
-        UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
-        scene->doFrame(i);
-        proxy->syncAndDrawFrame();
+        {
+            ATRACE_NAME("UI-Draw Frame");
+            UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+            scene->doFrame(i);
+            proxy->syncAndDrawFrame();
+        }
+        proxy->fence();
+        nsecs_t done = systemTime(CLOCK_MONOTONIC);
+        if (opts.reportFrametimeWeight) {
+            avgMs.add((done - vsync) / 1000000.0);
+            if (i % 10 == 9) {
+                printf("Average frametime %.3fms\n", avgMs.average());
+            }
+        }
     }
 
     proxy->dumpProfileInfo(STDOUT_FILENO, 0);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 619713c..1616a95 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -17,6 +17,7 @@
 #include "tests/common/TestScene.h"
 
 #include "protos/hwui.pb.h"
+#include "Properties.h"
 
 #include <getopt.h>
 #include <stdio.h>
@@ -25,26 +26,38 @@
 #include <unordered_map>
 #include <vector>
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::test;
 
-static int gFrameCount = 150;
 static int gRepeatCount = 1;
 static std::vector<TestScene::Info> gRunTests;
+static TestScene::Options gOpts;
 
 void run(const TestScene::Info& info, const TestScene::Options& opts);
 
 static void printHelp() {
-    printf("\
-USAGE: hwuitest [OPTIONS] <TESTNAME>\n\
-\n\
-OPTIONS:\n\
-  -c, --count=NUM      NUM loops a test should run (example, number of frames)\n\
-  -r, --runs=NUM       Repeat the test(s) NUM times\n\
-  -h, --help           Display this help\n\
-  --list               List all tests\n\
-\n");
+    printf(R"(
+USAGE: hwuitest [OPTIONS] <TESTNAME>
+
+OPTIONS:
+  -c, --count=NUM      NUM loops a test should run (example, number of frames)
+  -r, --runs=NUM       Repeat the test(s) NUM times
+  -h, --help           Display this help
+  --list               List all tests
+  --wait-for-gpu       Set this to wait for the GPU before producing the
+                       next frame. Note that without locked clocks this will
+                       pathologically bad performance due to large idle time
+  --report-frametime[=weight] If set, the test will print to stdout the
+                       moving average frametime. Weight is optional, default is 10
+  --cpuset=name        Adds the test to the specified cpuset before running
+                       Not supported on all devices and needs root
+)");
 }
 
 static void listTests() {
@@ -77,11 +90,56 @@
     }
 }
 
+static void moveToCpuSet(const char* cpusetName) {
+    if (access("/dev/cpuset/tasks", F_OK)) {
+        fprintf(stderr, "don't have access to cpusets, skipping...\n");
+        return;
+    }
+    static const int BUF_SIZE = 100;
+    char buffer[BUF_SIZE];
+
+    if (snprintf(buffer, BUF_SIZE, "/dev/cpuset/%s/tasks", cpusetName) >= BUF_SIZE) {
+        fprintf(stderr, "Error, cpusetName too large to fit in buffer '%s'\n", cpusetName);
+        return;
+    }
+    int fd = open(buffer, O_WRONLY | O_CLOEXEC);
+    if (fd == -1) {
+        fprintf(stderr, "Error opening file %d\n", errno);
+        return;
+    }
+    pid_t pid = getpid();
+
+    int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long) pid);
+    if (towrite >= BUF_SIZE) {
+        fprintf(stderr, "Buffer wasn't large enough?\n");
+    } else {
+        if (write(fd, buffer, towrite) != towrite) {
+            fprintf(stderr, "Failed to write, errno=%d", errno);
+        }
+    }
+    close(fd);
+}
+
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+    Reserved = 255,
+    List,
+    WaitForGpu,
+    ReportFrametime,
+    CpuSet,
+};
+}
+
 static const struct option LONG_OPTIONS[] = {
     { "frames", required_argument, nullptr, 'f' },
     { "repeat", required_argument, nullptr, 'r' },
     { "help", no_argument, nullptr, 'h' },
-    { "list", no_argument, nullptr, 'l' },
+    { "list", no_argument, nullptr, LongOpts::List },
+    { "wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu },
+    { "report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime },
+    { "cpuset", required_argument, nullptr, LongOpts::CpuSet },
     { 0, 0, 0, 0 }
 };
 
@@ -89,8 +147,6 @@
 
 void parseOptions(int argc, char* argv[]) {
     int c;
-    // temporary variable
-    int count;
     bool error = false;
     opterr = 0;
 
@@ -110,31 +166,53 @@
             // (although none of the current LONG_OPTIONS do this...)
             break;
 
-        case 'l':
+        case LongOpts::List:
             listTests();
             exit(EXIT_SUCCESS);
             break;
 
         case 'c':
-            count = atoi(optarg);
-            if (!count) {
+            gOpts.count = atoi(optarg);
+            if (!gOpts.count) {
                 fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
                 error = true;
-            } else {
-                gFrameCount = (count > 0 ? count : INT_MAX);
             }
             break;
 
         case 'r':
-            count = atoi(optarg);
-            if (!count) {
+            gRepeatCount = atoi(optarg);
+            if (!gRepeatCount) {
                 fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
                 error = true;
             } else {
-                gRepeatCount = (count > 0 ? count : INT_MAX);
+                gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX);
             }
             break;
 
+        case LongOpts::ReportFrametime:
+            if (optarg) {
+                gOpts.reportFrametimeWeight = atoi(optarg);
+                if (!gOpts.reportFrametimeWeight) {
+                    fprintf(stderr, "Invalid report frametime weight '%s'\n", optarg);
+                    error = true;
+                }
+            } else {
+                gOpts.reportFrametimeWeight = 10;
+            }
+            break;
+
+        case LongOpts::WaitForGpu:
+            Properties::waitForGpuCompletion = true;
+            break;
+
+        case LongOpts::CpuSet:
+            if (!optarg) {
+                error = true;
+                break;
+            }
+            moveToCpuSet(optarg);
+            break;
+
         case 'h':
             printHelp();
             exit(EXIT_SUCCESS);
@@ -172,13 +250,14 @@
 }
 
 int main(int argc, char* argv[]) {
+    // set defaults
+    gOpts.count = 150;
+
     parseOptions(argc, argv);
 
-    TestScene::Options opts;
-    opts.count = gFrameCount;
     for (int i = 0; i < gRepeatCount; i++) {
         for (auto&& test : gRunTests) {
-            run(test, opts);
+            run(test, gOpts);
         }
     }
     printf("Success!\n");
diff --git a/libs/hwui/tests/scripts/prep_volantis.sh b/libs/hwui/tests/scripts/prep_volantis.sh
new file mode 100755
index 0000000..09d4869
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_volantis.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+adb root
+adb wait-for-device
+adb shell stop mpdecision
+adb shell stop perfd
+adb shell stop
+for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do
+    adb shell kill $pid
+done
+adb shell setprop debug.egl.traceGpuCompletion 1
+adb shell daemonize surfaceflinger
+sleep 3
+adb shell setprop service.bootanim.exit 1
+
+# cpu possible frequencies
+# 204000 229500 255000 280500 306000 331500 357000 382500 408000 433500 459000
+# 484500 510000 535500 561000 586500 612000 637500 663000 688500 714000 739500
+# 765000 790500 816000 841500 867000 892500 918000 943500 969000 994500 1020000
+# 1122000 1224000 1326000 1428000 1530000 1632000 1734000 1836000 1938000
+# 2014500 2091000 2193000 2295000 2397000 2499000
+
+S=1326000
+echo "set cpu $cpu to $S hz";
+adb shell "echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed"
+
+#disable hotplug
+adb shell "echo 0 > /sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable"
+
+# gbus possible rates
+# 72000 108000 180000 252000 324000 396000 468000 540000 612000 648000
+# 684000 708000 756000 804000 852000 (kHz)
+
+S=324000000
+echo "set gpu to $s hz"
+adb shell "echo 1 > /d/clock/override.gbus/state"
+adb shell "echo $S > /d/clock/override.gbus/rate"