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