JankTracker有两个 一个是在CanvasContext 一个是在RenderThread中,用于dumpsys graphicsstats的输出
初始化函数,先分析hwc1,hwc2的fence机制有些不同
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.
mData = new ProfileData;
reset();
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
setFrameInterval(frameIntervalNanos);
}
构造函数,创建mData = new ProfileData;用于统计绘制信息,由于这里是初始化函数,先用reset重置统计,ProfileData的数据结构如下
struct ProfileData {
std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
// See comments on kBucket* constants for what this holds
std::array<uint32_t, 57> frameCounts;
// Holds a histogram of frame times in 50ms increments from 150ms to 5s
std::array<uint16_t, 97> slowFrameCounts;
uint32_t totalFrameCount;
uint32_t jankFrameCount;
nsecs_t statStartTime;
};
enum JankType {
kMissedVsync = 0,
kHighInputLatency,
kSlowUI,
kSlowSync,
kSlowRT,
// must be last
NUM_BUCKETS,
};
首先jankTypeCounts用于记录上面各种JankType的次数.上面的含义很好理解.
frameCounts用于记录不算特别早槽的帧的统计信息,主要就是绘制时间的统计信息,slowFrmeCounts就是用于统计耗时比较长的帧.
要了解frameCounts和slowFrmeCounts的含义还需要看下frameCountIndexForFrameTime()这个函数,参数frameTime代表绘制当前一帧所花的时间
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;
// 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;
}
这个算法很有意思,根据三个阀值计算frameCounts数组每个元素代表的实践步长,其中当frameTime
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);
}
也就是说HighInputLatency,kSlowUI,kSlowSync,kSlowRT的伐值分别定义为1.5帧,0.5帧,0.2帧,0.75帧,后面我们会看到这几种type分别代表什么含义
除了构造函数,主要暴露的接口就是void JankTracker::addFrame(const FrameInfo& frame) 接口,接下来的重点就是分析这个接口如何统计信息
void JankTracker::addFrame(const FrameInfo& frame) {
mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
if (mDequeueTimeForgiveness
&& frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
nsecs_t expectedDequeueDuration =
mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync]
- frame[FrameInfoIndex::IssueDrawCommandsStart];
if (expectedDequeueDuration > 0) {
// Forgive only up to the expected amount, but not more than
// the actual time spent blocked.
nsecs_t forgiveAmount = std::min(expectedDequeueDuration,
frame[FrameInfoIndex::DequeueBufferDuration]);
totalDuration -= forgiveAmount;
}
}
uint32_t framebucket = frameCountIndexForFrameTime(totalDuration);
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
mData->frameCounts[framebucket]++;
return;
}
// Only things like Surface.lockHardwareCanvas() are exempt from tracking
if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
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));
framebucket = std::max(framebucket, 0u);
mData->slowFrameCounts[framebucket]++;
}
mData->jankFrameCount++;
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->totalFrameCount计数,
frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted) 函数用于计算从开始处理vsync到处理完成的时间,计算结果保存在totalDuration中
另外mDequeueTimeForgiveness是用于hwc2的,我们先跳过if (mDequeueTimeForgiveness
&& frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) 条件成立的这段.
frameCountIndexForFrameTime这个函数我们前面已经介绍过它的原理.这里如果一帧的处理时间少于mFrameInterval(一般是16ms)就是正常的,直接给frameCounts中对应计数器+1,返回.
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));
framebucket = std::max(framebucket, 0u);
mData->slowFrameCounts[framebucket]++;
}
这一段我们重点看一下framebucket超过frameCounts能记录的情况,这种情况就要记录到slowFrameCounts中,这个数据每个元素记录50ms范围.
之后mData->jankFrameCount计数加1,代表有出现了一个jankFrame
static const Comparison COMPARISONS[] = {
{FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync},
{FrameInfoIndex::OldestInputEvent, FrameInfoIndex::Vsync},
{FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
{FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
{FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
};
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]++;
}
}
最后这段就是计算如上这些段的时间,然后统计到jankTypeCounts中.
这几段时间差值分别代表
1 main thread收到的vsync的时间到开始处理vsync的时间(发生在main thread)
2 最早的事件和开始处理vsync的时间 (发生在main thread)
3 main thread处理vsync到render thread开始同步的间隔(这里要减去发送命令给reader thread到开始同步的时间,也就是main thread真正处理的时间) (发生在main thread 和 render thread)
4 render thread开始同步到开始draw的时间(发生在main thread(同步完成后main thread基本会返回) 和 render thread)
5 render thread开始绘制到绘制完成的时间(发生在render thread中 极少情况main thread会阻塞在这里)
在回来和JankType对比一下
enum JankType {
kMissedVsync = 0,
kHighInputLatency,
kSlowUI,
kSlowSync,
kSlowRT,
// must be last
NUM_BUCKETS, };
再来看看阀值,细细品味
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);
到这里JankTracker的逻辑就分析完了
明天分析FrameInfoVisualizer