Initial attempt at jank-tracking stat collection
Is a bit naive, perhaps overly aggressive, but sorta works
Change-Id: I01a774e00dbe681439c02557d9728ae43c45ce50
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
new file mode 100644
index 0000000..62cb97c
--- /dev/null
+++ b/libs/hwui/JankTracker.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+#include "JankTracker.h"
+
+#include <cstdio>
+#include <inttypes.h>
+
+namespace android {
+namespace uirenderer {
+
+static const char* JANK_TYPE_NAMES[] = {
+ "Missed Vsync",
+ "High input latency",
+ "Slow UI thread",
+ "Slow bitmap uploads",
+ "Slow draw",
+};
+
+struct Comparison {
+ FrameInfoIndexEnum start;
+ FrameInfoIndexEnum end;
+};
+
+static const Comparison COMPARISONS[] = {
+ {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync},
+ {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync},
+ {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart},
+ {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart},
+ {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted},
+};
+
+// If the event exceeds 10 seconds throw it away, this isn't a jank event
+// it's an ANR and will be handled as such
+static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);
+
+/*
+ * Frames that are exempt from jank metrics.
+ * First-draw frames, for example, are expected to
+ * be slow, this is hidden from the user with window animations and
+ * other tricks
+ *
+ * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas()
+ * for now
+ *
+ * TODO: kSurfaceCanvas can negatively impact other drawing by using up
+ * time on the RenderThread, figure out how to attribute that as a jank-causer
+ */
+static const int64_t EXEMPT_FRAMES_FLAGS
+ = FrameInfoFlags::kWindowLayoutChanged
+ | FrameInfoFlags::kSurfaceCanvas;
+
+JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+ reset();
+ setFrameInterval(frameIntervalNanos);
+}
+
+void JankTracker::setFrameInterval(nsecs_t frameInterval) {
+ mFrameInterval = frameInterval;
+ mThresholds[kMissedVsync] = 1;
+ /*
+ * Due to interpolation and sample rate differences between the touch
+ * panel and the display (example, 85hz touch panel driving a 60hz display)
+ * we call high latency 1.5 * frameinterval
+ *
+ * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
+ * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
+ * Thus this must always be larger than frameInterval, or it will fail
+ */
+ mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
+
+ // Note that these do not add up to 1. This is intentional. It's to deal
+ // with variance in values, and should be sort of an upper-bound on what
+ // is reasonable to expect.
+ mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
+ mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
+ mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
+
+}
+
+void JankTracker::addFrame(const FrameInfo& frame) {
+ using namespace FrameInfoIndex;
+ mTotalFrameCount++;
+ // Fast-path for jank-free frames
+ int64_t totalDuration = frame[kFrameCompleted] - frame[kIntendedVsync];
+ if (CC_LIKELY(totalDuration < mFrameInterval)) {
+ return;
+ }
+
+ if (frame[kFlags] & EXEMPT_FRAMES_FLAGS) {
+ return;
+ }
+
+ mJankFrameCount++;
+
+ for (int i = 0; i < NUM_BUCKETS; i++) {
+ int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
+ if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
+ mBuckets[i].count++;
+ }
+ }
+}
+
+void JankTracker::dump(int fd) {
+ FILE* file = fdopen(fd, "a");
+ fprintf(file, "\nFrame stats:");
+ fprintf(file, "\n Total frames rendered: %u", mTotalFrameCount);
+ fprintf(file, "\n Janky frames: %u (%.2f%%)", mJankFrameCount,
+ (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f);
+ for (int i = 0; i < NUM_BUCKETS; i++) {
+ fprintf(file, "\n Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count);
+ }
+ fprintf(file, "\n");
+ fflush(file);
+}
+
+void JankTracker::reset() {
+ memset(mBuckets, 0, sizeof(JankBucket) * NUM_BUCKETS);
+ mTotalFrameCount = 0;
+ mJankFrameCount = 0;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */