http://blog.csdn.net/itfootball/article/details/43084527
http://blog.csdn.net/itfootball/article/details/43084527
版权声明:本文为Doctorq原创文章,未经博主允许不得转载。
关键点
在testerhome看到一个好的帖子,说的是fps的获取方式,值得好好研究一下。
获取的方式是通过下面的命令获取
命令意义
上面的命令是做什么的?
可以看看老罗的关于SurfaceFlinger的详细讲解,那我这里只是简单的描述一下:
SurfaceFlinger是一个系统服务,管理Android帧缓冲区,了解这些就足够啦,因为我们要获得的FPS值(Frames Per Second)中文翻译过来是每秒钟填充图像的帧数。
ok,那我们知道了这个服务的作用。
命令的结果
我们来看一下这个命令的结果,取android系统的主界面的帧数据
是不是有点晕,这些数字都是啥跟啥啊。其实testerhome上面的那篇文章提供了获取fps方法具体解释
用到了一个第三方的库:pylib
上面的解释信息有如下主要信息:
数据的单位是纳秒,时间是以开机时间为起始点。
每一次的命令都会得到128行的帧相关的数据。
第一行数据,表示刷新的时间间隔refresh_period,我的机器打印出来的间隔期是:
那么剩下来127行的数据分为3部分,每一行的数据的每一列都代表一部分。
第一部分
这一部分的数据表示应用程序绘制图像的时间点。
第二部分
在SF(软件)将帧提交给H/W(硬件)绘制之前的垂直同步时间。
第三部分
在SF将帧提交给H/W的时间点,算是H/W接受完SF发来数据的时间点,绘制完成的时间点。
那么可以看出第一部分和第三部分类似,那么差异在于哪里?差异在于帧有延迟时间,从准备好绘制完成绘制的时间间隔就是帧延迟。
何为jank,即掉帧 。每一行都可以通过下面的公式得到一个值,该值是一个标准,我们称为jankflag,如果当前行的jankflag与上一行的jankflag发生改变,那么就叫掉帧。
所以掉帧是一个状态。
FPS计算
那么我们要用到上面的哪些数据了?那么我们去库里面一步一步去按照方法来找到最后的答案,首先它是调用了下面方法:
那么我们首先找到SurfaceStatsCollector这个类,在pylib库中的perl包下
SampleResults方法:
首先调用了 _StorePerfResults 方法
从上面看出计算方法有两种方式,一个是支持legacy方法,一个不支持legacy方法。
第一种情况
支持legacy方法。判断是否支持legacy方法,可以通过执行dumpsys SurfaceFlinger --latency-clear SurfaceView来判断。
然后我们进入
这个_GetSurfaceStatsLegacy方法会调用"service call SurfaceFlinger 1013"这个命令,结果是如下形式:
然后我们会提取里面的00000b5e这个16进制的数,然后通过int('str',16)方法将16进制数转化为10进制的数,这个值就是当前surface的索引值。可以通过2个不同索引值的surface之间的间隔时间获得。然后返回一个字典,里面包含了2个内容:当前surface索引,以及当前时间戳。这个方法就结束了。该字典将赋值给surface_after。我们就回到了_StorePerfResults方法中,看下一行要执行的代码:
上面的代码,是将这次的时间-上次的时间,得到的值就是2次获取surface索引的时间间隔,赋值给td,然后取得td的秒数,但是精确到微妙级别。赋值给seconds。
然后计算2次surface索引值的差值,得到的值就是在seconds时间内产生surface的个数。赋值给frame_count。
然后用frame_count/seconds公式计算,做4舍5入,最后转化为整形,追加到results数组中,该方法就返回了。就到了SampleResults方法中:
由于上面的第二行可以看出来,每次的results的数组都会重新设置为空,那么当前results的值就只有一个值,就是我们这次所获得值,所以返回的results数组里就只有一个数值。就是我们的fps的值,然后至于你获得多少次这样的值,以及间隔时间,那是你自己决定的事啦。
what?
有人看完上面的内容,有蒙的感觉么?其实我也蒙了,我们说了那么多的dumpsys SurfaceFlinger --latency,但是经过我们一分析,居然真正的计算是没有用到这个里面的数据的,是不是很奔溃。但是要想到的一点是,在调用collector.SampleResults()方法前,是需要启动_CollectorThread(self)的,尝试执行dumpsys命令3次获得需要的值。该线程中的就是调用了
_GetSurfaceFlingerFrameData()里的方法,具体实现细节如下代码所示。
_GetSurfaceFlingerFrameData方法实现细节如下:
上面的具体执行流程,是先运行dumpsys SurfaceFlinger --latency 命令,从得到的128行数据中,提取第一行的刷新时间,然后得到每一行的中第二部分数据,获取其中秒数,保存到timestamps数组中。然后方法返回包含刷新时间refresh_period和timestamps的数组。然后回到线程的主方法中。
1.判断获取到的数据为NONE
如果尝试的次数没到3次,再执行一遍dumpsys命令。
如果超过了3次,我们要判断上一次获取的timestamps数组中最后一个值是否为0。因为last_timestamp的赋值语句如下:
如果不为0,说明我们已经收集过了,直接在队列中加上NONE值,让线程等待,跳出循环了。否则抛出异常
2.不为NONE
如果为0,说明我们fps的数据还没有开始收集,我们会将_GetSurfaceFlingerFrameData方法返回的数组中元素一个一个赋值到我们本地数组中。然后给last_timestamp赋值。
然后我们会向_data_queue队列中添加refresh_period, timestamps数据。
总结
从上面的分析可知,要想获得获取fps值,需要3步:
1.adb shell dumpsys SurfaceFlinger --latency命令产生fps数据
2.通过service call SurfaceFlinger 1013 来得到当前帧的索引以及时间戳,设置为A = {indexA,timeA}
3.公式:
设上一次的数据为B = {indexB,timeB}
FPS = (indexA-indexB)/(timeA-timeB)
第二种情况
当--latency-clear不能使用,也就是`service call SurfaceFlinger 1013
`命令不能使用,那自然上面的方法就不起作用了。这个时候我们就要从下面的代码进行分析了:
首先我们从线程中获得dumpsys命令得到的值,然后创建Result对象,该对象中含有属性名和属性值,以及单位。
然后我们要进入_CalculateResults和_CalculateBuckets方法。
_CalculateResults
上面的方法中,首先得到帧的数量frame_count,然后得到产生frame_count所用的时间seconds。然后调用_GetNormalizedDeltas方法
上面巧妙的使用zip来计算各个数据之间的差值。由于_MIN_NORMALIZED_FRAME_LENGTH =0.5,所以要执行后续的语句,filter函数中,从deltas数组中取出元素除以refresh_period,判断杯除后的值是否大于0.5,这个函数作用过滤掉被除后小于0.5的值。那么我们最终返回的值就是这个数组deltas,以及数组中每个元素除以refresh_period后的生成的新的数组。然后回到_CalculateResults方法中,差值数组赋值给frame_lengths,新的数组赋值给normalized_frame_lengths。然后对frame_lengths的个数进行判断。然后我们再调用一次_GetNormalizedDeltas,这个时候传入的min_normalized_delta是空的,所以不会执行filter函数。直接求出frame_lengths数组中各个元素的差值保存到数组deltas中。然后再计算deltas的值与refresh_period比值,这是为了求jank(掉帧)。然后方法返回,将deltas值赋给length_changes,将比值赋给normalized_changes。
为了求出jank,我们需要求出normalized_changes数组中比0大的数。下面的代码就是求出jank_count代码块。
jank_count初始值设为,然后遍历jankiness,求出大于0,小于20的数的个数。这个值就是jank的值,掉帧值。而fps的值是通过下面公式得到的:
这样我们就得到了fps和jank,然后fps-jank 就是我们要得到的数。
_CalculateBuckets
这个地方是帮助你去头去尾后的数据。
总结
当无法使用--latency--clear方法的时候,我们需要计算fps和jank的值,
fps的值计算公司变为int(round((frame_count-1)/ seconds)),
而且还可以得到吊帧的个数
感谢
testerhome_小A帮助我分析Python相关源码
kasi前辈的补充
其他计算fps的方法
http://wuche.info/android-fps/