背景:
hi,粉丝朋友们:
大家好!上次视频发布了一个实时fps的实现成果展示如下:
相关介绍视频如下:
https://mp.weixin.qq.com/s/CvQ3-MlEHAC-Wez7pCiuIg
下面就来开始介绍一下这个实时fps实现的两个核心部分
分为实时fps的计算,也就是最核心的部分,再接下来是如何展示fps的ui数字
实时fps帧率的计算
在SurfaceFlinger中FrameTimeline类中有一个现成的计算fps的方法如下:
//重点参数就是layerIds,这个代表就是每个layer即每个task的图层的id,即该接口是统计每个layer的对于刷新率
float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
if (layerIds.empty()) {
return 0.0f;
}
std::vector<nsecs_t> presentTimes;
{
std::scoped_lock lock(mMutex);
//初始化话presentTimes这个集合
presentTimes.reserve(mDisplayFrames.size());
//遍历当前系统中所有的mDisplayFrames数据
for (size_t i = 0; i < mDisplayFrames.size(); i++) {
const auto& displayFrame = mDisplayFrames[i];
if (displayFrame->getActuals().presentTime <= 0) {
continue;
}
//获取每个DisplayFrame的对应surfaceFrame
for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
//如果该surfaceFrame已经处于Presented已经显示,而且刚好layerIds又是属于要计算的
if ((surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented &&
layerIds.count(surfaceFrame->getLayerId()) > 0 )) {
// We're looking for DisplayFrames that presents at least one layer from
// layerIds, so push the present time and skip looking through the rest of the
// SurfaceFrames.
//把displayFrame的presentTime时间放入到了presentTimes集合
presentTimes.push_back(displayFrame->getActuals().presentTime);
break;
}
}
}
}
// FPS can't be computed when there's fewer than 2 presented frames.
if (presentTimes.size() <= 1) {
return 0.0f;
}
nsecs_t priorPresentTime = -1;
nsecs_t totalPresentToPresentWalls = 0;
//遍历前面的presentTimes集合
for (const nsecs_t presentTime : presentTimes) {
if (priorPresentTime == -1) {
priorPresentTime = presentTime;//首先获取了第一个presentTime作为基点
continue;
}
//开始统计所有绘制帧的总时间
totalPresentToPresentWalls += (presentTime - priorPresentTime);
priorPresentTime = presentTime;
}
//1秒的时间变成ns
const constexpr nsecs_t kOneSecond =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
// (10^9 nanoseconds / second) * (N present deltas) / (total nanoseconds in N present deltas) =
// M frames / second
//这里是核心计算公式,直接看肯定比较难懂,转换成更好理解公式
//perFrameTime =totalPresentToPresentWalls / (presentTimes.size() - 1)
//这个代表过去一段时间内,平均每一帧花费时间,1秒时间 / perFrameTime = 一秒绘制的帧数fps
return kOneSecond * static_cast<nsecs_t>((presentTimes.size() - 1)) /
static_cast<float>(totalPresentToPresentWalls);
}
代码其实还算不太难,主要分为以下几个部分:
1、统计出对应layerID的已经处于已经显示的presentTimes的集合
2、根据统计的presentTimes的集合,计算出相关的总耗时
3、根据计算公式计算出来了一秒钟的平均可以绘制的fps
实时fps帧率的ui展示
说说线索,首先就是android系统其实自带了一个刷新的显示按钮,不过这个刷新率是静态的,他只是来展示一下手机屏幕的刷新率,因为目前很多手机都是支持多种刷新率,比如120-60hz都是支持的,所以这样就可能目前屏幕的刷新率可以有不同档次,所以这个刷新率按钮主要就是显示一下目前手机的刷新情况而已,并不是说统计帧手机系统每个画面的实时刷新率,即这个按钮开启后,只是永远固定显示手机刷新率比如60,90,120
开启后如下:
所以就是顺着这条思路去探索,这个数字如何显示的。
具体追查到的结果如下:
核心代码路径:
frameworks/native/services/surfaceflinger/RefreshRateOverlay.h
frameworks/native/services/surfaceflinger/RefreshRateOverlay.cpp
其实本质上就是建立了一个顶部图层,然后用这个图层绘制相关数字:
dump出来也是一样的:
总结方案
1、利用FrameTimeLine来获取实时fps的数据
2、直接使用SurfaceFlinger中的RefreshRateOverlay这个图层类来负责显示这个实时fps数据
本文章更多详细代码和资料需要购买课程获取
hal+perfetto+surfaceflinger
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
私聊作者+v(androidframework007)
其他课程七件套专题:
点击这里
https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw