blob: 46b094556ad7ed8d0cc58c49fa164918ff8b5e8d [file] [log] [blame]
John Reckba6adf62015-02-19 14:36:50 -08001/*
2 * Copyright (C) 2015 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#include "JankTracker.h"
17
John Recke70c5752015-03-06 14:40:50 -080018#include <algorithm>
John Reckba6adf62015-02-19 14:36:50 -080019#include <cstdio>
20#include <inttypes.h>
21
22namespace android {
23namespace uirenderer {
24
25static const char* JANK_TYPE_NAMES[] = {
26 "Missed Vsync",
27 "High input latency",
28 "Slow UI thread",
29 "Slow bitmap uploads",
30 "Slow draw",
31};
32
33struct Comparison {
John Reckc87be992015-02-20 10:57:22 -080034 FrameInfoIndex start;
35 FrameInfoIndex end;
John Reckba6adf62015-02-19 14:36:50 -080036};
37
38static const Comparison COMPARISONS[] = {
39 {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync},
40 {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync},
41 {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart},
42 {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart},
43 {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted},
44};
45
46// If the event exceeds 10 seconds throw it away, this isn't a jank event
47// it's an ANR and will be handled as such
48static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);
49
50/*
51 * Frames that are exempt from jank metrics.
52 * First-draw frames, for example, are expected to
53 * be slow, this is hidden from the user with window animations and
54 * other tricks
55 *
56 * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas()
57 * for now
58 *
59 * TODO: kSurfaceCanvas can negatively impact other drawing by using up
60 * time on the RenderThread, figure out how to attribute that as a jank-causer
61 */
62static const int64_t EXEMPT_FRAMES_FLAGS
63 = FrameInfoFlags::kWindowLayoutChanged
64 | FrameInfoFlags::kSurfaceCanvas;
65
66JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
67 reset();
68 setFrameInterval(frameIntervalNanos);
69}
70
71void JankTracker::setFrameInterval(nsecs_t frameInterval) {
72 mFrameInterval = frameInterval;
73 mThresholds[kMissedVsync] = 1;
74 /*
75 * Due to interpolation and sample rate differences between the touch
76 * panel and the display (example, 85hz touch panel driving a 60hz display)
77 * we call high latency 1.5 * frameinterval
78 *
79 * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
80 * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
81 * Thus this must always be larger than frameInterval, or it will fail
82 */
83 mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
84
85 // Note that these do not add up to 1. This is intentional. It's to deal
86 // with variance in values, and should be sort of an upper-bound on what
87 // is reasonable to expect.
88 mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
89 mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
90 mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
91
92}
93
94void JankTracker::addFrame(const FrameInfo& frame) {
John Reckba6adf62015-02-19 14:36:50 -080095 mTotalFrameCount++;
96 // Fast-path for jank-free frames
John Reckc87be992015-02-20 10:57:22 -080097 int64_t totalDuration =
98 frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync];
John Recke70c5752015-03-06 14:40:50 -080099 uint32_t framebucket = std::min(
100 static_cast<typeof sizeof(mFrameCounts)>(ns2ms(totalDuration)),
101 sizeof(mFrameCounts) / sizeof(mFrameCounts[0]));
102 // Keep the fast path as fast as possible.
John Reckba6adf62015-02-19 14:36:50 -0800103 if (CC_LIKELY(totalDuration < mFrameInterval)) {
John Recke70c5752015-03-06 14:40:50 -0800104 mFrameCounts[framebucket]++;
John Reckba6adf62015-02-19 14:36:50 -0800105 return;
106 }
107
John Reckc87be992015-02-20 10:57:22 -0800108 if (frame[FrameInfoIndex::kFlags] & EXEMPT_FRAMES_FLAGS) {
John Reckba6adf62015-02-19 14:36:50 -0800109 return;
110 }
111
John Recke70c5752015-03-06 14:40:50 -0800112 mFrameCounts[framebucket]++;
John Reckba6adf62015-02-19 14:36:50 -0800113 mJankFrameCount++;
114
115 for (int i = 0; i < NUM_BUCKETS; i++) {
116 int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
117 if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
118 mBuckets[i].count++;
119 }
120 }
121}
122
123void JankTracker::dump(int fd) {
124 FILE* file = fdopen(fd, "a");
125 fprintf(file, "\nFrame stats:");
126 fprintf(file, "\n Total frames rendered: %u", mTotalFrameCount);
127 fprintf(file, "\n Janky frames: %u (%.2f%%)", mJankFrameCount,
128 (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f);
John Recke70c5752015-03-06 14:40:50 -0800129 fprintf(file, "\n 90th percentile: %ums", findPercentile(90));
130 fprintf(file, "\n 95th percentile: %ums", findPercentile(95));
131 fprintf(file, "\n 99th percentile: %ums", findPercentile(99));
John Reckba6adf62015-02-19 14:36:50 -0800132 for (int i = 0; i < NUM_BUCKETS; i++) {
133 fprintf(file, "\n Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count);
134 }
135 fprintf(file, "\n");
136 fflush(file);
137}
138
139void JankTracker::reset() {
John Recke70c5752015-03-06 14:40:50 -0800140 memset(mBuckets, 0, sizeof(mBuckets));
141 memset(mFrameCounts, 0, sizeof(mFrameCounts));
John Reckba6adf62015-02-19 14:36:50 -0800142 mTotalFrameCount = 0;
143 mJankFrameCount = 0;
144}
145
John Recke70c5752015-03-06 14:40:50 -0800146uint32_t JankTracker::findPercentile(int percentile) {
147 int pos = percentile * mTotalFrameCount / 100;
148 int remaining = mTotalFrameCount - pos;
149 for (int i = sizeof(mFrameCounts) / sizeof(mFrameCounts[0]) - 1; i >= 0; i--) {
150 remaining -= mFrameCounts[i];
151 if (remaining <= 0) {
152 return i;
153 }
154 }
155 return 0;
156}
157
John Reckba6adf62015-02-19 14:36:50 -0800158} /* namespace uirenderer */
159} /* namespace android */