Split out jank data from policy
Move ProfileData out to its own file with helper
accessors. This keeps policy (what is/isn't jank)
outside of the data storage.
Also use lambdas to iterate over the histogram
to make it nicer for dumping & proto-ifying.
Test: hwui_unit_tests pass & jank data still dumps
Change-Id: I88488369ec77590a2867f51128e65bb786aa34e6
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 028d9f7..f9671ed4 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -34,14 +34,6 @@
namespace android {
namespace uirenderer {
-static const char* JANK_TYPE_NAMES[] = {
- "Missed Vsync",
- "High input latency",
- "Slow UI thread",
- "Slow bitmap uploads",
- "Slow issue draw commands",
-};
-
struct Comparison {
FrameInfoIndex start;
FrameInfoIndex end;
@@ -68,65 +60,11 @@
*/
static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;
-// The bucketing algorithm controls so to speak
-// If a frame is <= to this it goes in bucket 0
-static const uint32_t kBucketMinThreshold = 5;
-// If a frame is > this, start counting in increments of 2ms
-static const uint32_t kBucket2msIntervals = 32;
-// If a frame is > this, start counting in increments of 4ms
-static const uint32_t kBucket4msIntervals = 48;
-
// For testing purposes to try and eliminate test infra overhead we will
// consider any unknown delay of frame start as part of the test infrastructure
// and filter it out of the frame profile data
static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;
-// The interval of the slow frame histogram
-static const uint32_t kSlowFrameBucketIntervalMs = 50;
-// The start point of the slow frame bucket in ms
-static const uint32_t kSlowFrameBucketStartMs = 150;
-
-// This will be called every frame, performance sensitive
-// Uses bit twiddling to avoid branching while achieving the packing desired
-static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime) {
- uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
- // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
- // of negating 1 (twos compliment, yaay) else mask will be 0
- uint32_t mask = -(index > kBucketMinThreshold);
- // If index > threshold, this will essentially perform:
- // amountAboveThreshold = index - threshold;
- // index = threshold + (amountAboveThreshold / 2)
- // However if index is <= this will do nothing. It will underflow, do
- // a right shift by 0 (no-op), then overflow back to the original value
- index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals))
- + kBucket4msIntervals;
- index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals))
- + kBucket2msIntervals;
- // If index was < minThreshold at the start of all this it's going to
- // be a pretty garbage value right now. However, mask is 0 so we'll end
- // up with the desired result of 0.
- index = (index - kBucketMinThreshold) & mask;
- return index;
-}
-
-// Only called when dumping stats, less performance sensitive
-int32_t JankTracker::frameTimeForFrameCountIndex(uint32_t index) {
- index = index + kBucketMinThreshold;
- if (index > kBucket2msIntervals) {
- index += (index - kBucket2msIntervals);
- }
- if (index > kBucket4msIntervals) {
- // This works because it was already doubled by the above if
- // 1 is added to shift slightly more towards the middle of the bucket
- index += (index - kBucket4msIntervals) + 1;
- }
- return index;
-}
-
-int32_t JankTracker::frameTimeForSlowFrameCountIndex(uint32_t index) {
- return (index * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs;
-}
-
JankTracker::JankTracker(const DisplayInfo& displayInfo) {
// By default this will use malloc memory. It may be moved later to ashmem
// if there is shared space for it and a request comes in to do that.
@@ -199,29 +137,7 @@
return;
}
- // The new buffer may have historical data that we want to build on top of
- // But let's make sure we don't overflow Just In Case
- uint32_t divider = 0;
- if (newData->totalFrameCount > (1 << 24)) {
- divider = 4;
- }
- for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) {
- newData->jankTypeCounts[i] >>= divider;
- newData->jankTypeCounts[i] += mData->jankTypeCounts[i];
- }
- for (size_t i = 0; i < mData->frameCounts.size(); i++) {
- newData->frameCounts[i] >>= divider;
- newData->frameCounts[i] += mData->frameCounts[i];
- }
- newData->jankFrameCount >>= divider;
- newData->jankFrameCount += mData->jankFrameCount;
- newData->totalFrameCount >>= divider;
- newData->totalFrameCount += mData->totalFrameCount;
- if (newData->statStartTime > mData->statStartTime
- || newData->statStartTime == 0) {
- newData->statStartTime = mData->statStartTime;
- }
-
+ newData->mergeWith(*mData);
freeData();
mData = newData;
mIsMapped = true;
@@ -251,7 +167,6 @@
}
void JankTracker::addFrame(const FrameInfo& frame) {
- mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
if (mDequeueTimeForgiveness
@@ -271,11 +186,10 @@
}
}
LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
- uint32_t framebucket = frameCountIndexForFrameTime(totalDuration);
- LOG_ALWAYS_FATAL_IF(framebucket < 0, "framebucket < 0 (%u)", framebucket);
+ mData->reportFrame(totalDuration);
+
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
- mData->frameCounts[framebucket]++;
return;
}
@@ -284,22 +198,12 @@
return;
}
- if (framebucket <= mData->frameCounts.size()) {
- mData->frameCounts[framebucket]++;
- } else {
- framebucket = (ns2ms(totalDuration) - kSlowFrameBucketStartMs)
- / kSlowFrameBucketIntervalMs;
- framebucket = std::min(framebucket,
- static_cast<uint32_t>(mData->slowFrameCounts.size() - 1));
- mData->slowFrameCounts[framebucket]++;
- }
-
- mData->jankFrameCount++;
+ mData->reportJank();
for (int i = 0; i < NUM_BUCKETS; i++) {
int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
- mData->jankTypeCounts[i]++;
+ mData->reportJankType((JankType) i);
}
}
}
@@ -320,58 +224,16 @@
if (sFrameStart != FrameInfoIndex::IntendedVsync) {
dprintf(fd, "\nNote: Data has been filtered!");
}
- dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime);
- 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));
- for (int i = 0; i < NUM_BUCKETS; i++) {
- dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
- }
- dprintf(fd, "\nHISTOGRAM:");
- for (size_t i = 0; i < data->frameCounts.size(); i++) {
- dprintf(fd, " %ums=%u", frameTimeForFrameCountIndex(i),
- data->frameCounts[i]);
- }
- for (size_t i = 0; i < data->slowFrameCounts.size(); i++) {
- dprintf(fd, " %ums=%u", frameTimeForSlowFrameCountIndex(i),
- data->slowFrameCounts[i]);
- }
+ data->dump(fd);
dprintf(fd, "\n");
}
void JankTracker::reset() {
- mData->jankTypeCounts.fill(0);
- mData->frameCounts.fill(0);
- mData->slowFrameCounts.fill(0);
- mData->totalFrameCount = 0;
- mData->jankFrameCount = 0;
- mData->statStartTime = systemTime(CLOCK_MONOTONIC);
+ mData->reset();
sFrameStart = Properties::filterOutTestOverhead
? FrameInfoIndex::HandleInputStart
: FrameInfoIndex::IntendedVsync;
}
-uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
- int pos = percentile * data->totalFrameCount / 100;
- int remaining = data->totalFrameCount - pos;
- for (int i = data->slowFrameCounts.size() - 1; i >= 0; i--) {
- remaining -= data->slowFrameCounts[i];
- if (remaining <= 0) {
- return (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs;
- }
- }
- for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
- remaining -= data->frameCounts[i];
- if (remaining <= 0) {
- return frameTimeForFrameCountIndex(i);
- }
- }
- return 0;
-}
-
} /* namespace uirenderer */
} /* namespace android */