diff options
Diffstat (limited to 'libs/hwui/ProfileData.cpp')
-rw-r--r-- | libs/hwui/ProfileData.cpp | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp new file mode 100644 index 000000000000..a295c5debc67 --- /dev/null +++ b/libs/hwui/ProfileData.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2017 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 "ProfileData.h" + +#include <cinttypes> + +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", +}; + +// 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; + +// 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 +uint32_t ProfileData::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; +} + +uint32_t ProfileData::frameTimeForSlowFrameCountIndex(uint32_t index) { + return (index * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs; +} + +void ProfileData::mergeWith(const ProfileData& other) { + // Make sure we don't overflow Just In Case + uint32_t divider = 0; + if (mTotalFrameCount > (1 << 24)) { + divider = 4; + } + for (size_t i = 0; i < other.mJankTypeCounts.size(); i++) { + mJankTypeCounts[i] >>= divider; + mJankTypeCounts[i] += other.mJankTypeCounts[i]; + } + for (size_t i = 0; i < other.mFrameCounts.size(); i++) { + mFrameCounts[i] >>= divider; + mFrameCounts[i] += other.mFrameCounts[i]; + } + mJankFrameCount >>= divider; + mJankFrameCount += other.mJankFrameCount; + mTotalFrameCount >>= divider; + mTotalFrameCount += other.mTotalFrameCount; + if (mStatStartTime > other.mStatStartTime + || mStatStartTime == 0) { + mStatStartTime = other.mStatStartTime; + } +} + +void ProfileData::dump(int fd) const { + dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime); + dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount); + dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount, + (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f); + dprintf(fd, "\n50th percentile: %ums", findPercentile(50)); + dprintf(fd, "\n90th percentile: %ums", findPercentile(90)); + dprintf(fd, "\n95th percentile: %ums", findPercentile(95)); + dprintf(fd, "\n99th percentile: %ums", findPercentile(99)); + for (int i = 0; i < NUM_BUCKETS; i++) { + dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], mJankTypeCounts[i]); + } + dprintf(fd, "\nHISTOGRAM:"); + histogramForEach([fd](HistogramEntry entry) { + dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount); + }); +} + +uint32_t ProfileData::findPercentile(int percentile) const { + int pos = percentile * mTotalFrameCount / 100; + int remaining = mTotalFrameCount - pos; + for (int i = mSlowFrameCounts.size() - 1; i >= 0; i--) { + remaining -= mSlowFrameCounts[i]; + if (remaining <= 0) { + return (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs; + } + } + for (int i = mFrameCounts.size() - 1; i >= 0; i--) { + remaining -= mFrameCounts[i]; + if (remaining <= 0) { + return frameTimeForFrameCountIndex(i); + } + } + return 0; +} + +void ProfileData::reset() { + mJankTypeCounts.fill(0); + mFrameCounts.fill(0); + mSlowFrameCounts.fill(0); + mTotalFrameCount = 0; + mJankFrameCount = 0; + mStatStartTime = systemTime(CLOCK_MONOTONIC); +} + +void ProfileData::reportFrame(int64_t duration) { + mTotalFrameCount++; + uint32_t framebucket = frameCountIndexForFrameTime(duration); + if (framebucket <= mFrameCounts.size()) { + mFrameCounts[framebucket]++; + } else { + framebucket = (ns2ms(duration) - kSlowFrameBucketStartMs) / kSlowFrameBucketIntervalMs; + framebucket = std::min(framebucket, static_cast<uint32_t>(mSlowFrameCounts.size() - 1)); + mSlowFrameCounts[framebucket]++; + } +} + +void ProfileData::histogramForEach(const std::function<void(HistogramEntry)>& callback) const { + for (size_t i = 0; i < mFrameCounts.size(); i++) { + callback(HistogramEntry{frameTimeForFrameCountIndex(i), mFrameCounts[i]}); + } + for (size_t i = 0; i < mSlowFrameCounts.size(); i++) { + callback(HistogramEntry{frameTimeForSlowFrameCountIndex(i), mSlowFrameCounts[i]}); + } +} + +} /* namespace uirenderer */ +} /* namespace android */
\ No newline at end of file |