diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index eb9b55f196bbc..c305f65db6016 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -243,6 +243,7 @@ void JankTracker::dumpData(const ProfileData* data, int fd) { 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 0669596b21ca6..083aeb7ed5858 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -53,6 +53,8 @@ int Properties::overrideSpotShadowStrength = -1; 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 1dde7e054aabd..88f1dbc989261 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -285,6 +285,9 @@ public: 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 78df29759a443..466fef9def097 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::loadConfig() { } 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 @@ void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) { 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 df8d194f641b3..706f2ff752221 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -37,6 +37,7 @@ class TestScene { public: struct Options { int count = 0; + int reportFrametimeWeight = 0; }; template diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 82612206cac82..a843e9265ef1a 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -38,6 +38,30 @@ public: } }; +template +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 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { 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 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 619713c58c9af..1616a95857d87 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 #include @@ -25,26 +26,38 @@ #include #include +#include +#include +#include +#include + using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::test; -static int gFrameCount = 150; static int gRepeatCount = 1; static std::vector gRunTests; +static TestScene::Options gOpts; void run(const TestScene::Info& info, const TestScene::Options& opts); static void printHelp() { - printf("\ -USAGE: hwuitest [OPTIONS] \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] + +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 listTests() { } } +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 @@ static const char* SHORT_OPTIONS = "c:r:h"; void parseOptions(int argc, char* argv[]) { int c; - // temporary variable - int count; bool error = false; opterr = 0; @@ -110,31 +166,53 @@ void parseOptions(int argc, char* argv[]) { // (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 @@ void parseOptions(int argc, char* argv[]) { } 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 0000000000000..09d4869523ba6 --- /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"