Android Graphics 显示系统 - 计算FPS的原理与探秘Present Fence

“ 最近忙着新房子装修的事情,这篇计划内的文章拖了好久一直没有足够的时间来写作,终于挤出些儿时间来继续研究学习了。

整了四个晚上终于拼凑出一篇文章,虽说是讲FPS计算原理,但该文涉及的知识点还是蛮多的,特别是对于present fence的来龙去脉重点做了分析,还牵涉到一些合成&HWC的逻辑。全文5000多字,内容丰满。一个业余爱好者的拙劣认知,请大家审慎阅读!

01

前言

FPS即Frames Per Second,表示每秒显示的帧数。作为衡量UI流畅度、视频播放性能、游戏性能的基础指标,开发者通常会观察FPS来评价应用是否有掉帧/卡顿等性能问题。前面我们分享了两篇计算FPS的文章:

Android Graphics 显示系统 - 监测、计算FPS的工具及设计分析

Android Graphics 显示系统 - 通过dumpsys SurfaceFlinger --latency计算FPS

那这些方法计算FPS的原理是什么呢?本篇我们就着手探索下利用dumpsys SurfaceFlinger --latency计算FPS的原理

02

Fence的概念

在介绍FPS计算原理前,需要先理解Android中Fence的概念。在前面的文章中我们有对Fence做过讲解,这里我们就简略赘述。

Android Graphics 显示系统 - GraphicBuffer同步机制-Fence(二十)

Fence是什么呢?

Fence,直译为“栅栏”,用来把某个东西拦截住。顾名思义,它是一种同步机制:把东西拦截住,又在合适的时机放行,达到同步的效果。

CPU、GPU、HWC是异步工作的,所以需要Fence这种同步机制来协调他们的工作。

那么Fence要拦住什么东西呢?

就是GraphicBuffer。GraphicBuffer中存放了应用绘制好的待要显示的数据,GraphicBuffer在整个绘制、合成、显示的过程中一直在 CPU、GPU 和 HWC 之前传递,某一方要使用GraphicBuffer之前,需要先检查上一个使用者是否已经移交了GraphicBuffer的“使用权”。而这里的“使用权”,就是Fence。当Fence 释放(即signal)的时候,说明GraphicBuffer的上一个使用者已经交出了使用权,此时对于GraphicBuffer 进行操作(read or write)是安全的。

Android中有哪些Fence呢?

在Android里面,总共有3类Fence:

  • acquire fence

    生产者(App)将GraphicBuffer通过queueBuffer()还给BufferQueue的时候,此时异步工作的GPU可能还没有完成绘制,此时会带上一个Fence,这个 Fence就是acquire fence。当消费者(SurfaceFlinger/HWC)要读取GraphicBuffer以进行合成操作的时候,需要等acquire fence释放之后才行。

    acquire fence就是生产者用来告诉消费者生产数据完成的同步信号!

  • release fence

    当生产者(App)通过dequeueBuffer()从BufferQueue申请到一块GraphicBuffer,要对GraphicBuffer进行绘制(写操作)的时候,需要保证消费者(上一个使用者)已经不再使用这个GraphicBuffer了,即需要等release fence signal才能对这块GraphicBuffer进行写操作。

    acquire fence就是消费者用来告诉生产者Buffer中的数据我已消费完毕的信号!

  • present fence 

    当前帧成功显示到屏幕的时候,present fence就会signal。

解释比较简略,建议大家阅读Android官网的英文解读,从不同角度并结合API理解

https://source.android.com/docs/core/graphics/sync

图片

03

计算Layer FPS的方法

计算Layer FPS的方法我们前面提供了两种方法,本篇我们主要分析利用dumpsys命令计算FPS的原理:

dumpsys SurfaceFlinger --latency Layer-name

让我们看看这条命令执行后会输出什么内容呢?

以播放Youtube视频时抓取信息为例:

首先,看看当前可见图层的状况

$ adb shell dumpsys SurfaceFlinger --hwclayers
Display 4629995505126966272 (active) HWC layers:
---------------------------------------------------------------------------------------------------------------------------------------------------------------
 Layer name
           Z |  Window Type |  Comp Type |  Transform |   Disp Frame (LTRB) |          Source Crop (LTRB) |     Frame Rate (Explicit) (Seamlessness) [Focused]
---------------------------------------------------------------------------------------------------------------------------------------------------------------
 SurfaceView[com.google.android.youtu[...].tv.activity.MainActivity](BLAST)#99
  rel      0 |            0 |     DEVICE |          0 |    0    0 1920 1080 |    0.0    0.0 1920.0 1080.0 |                                              [*]
---------------------------------------------------------------------------------------------------------------------------------------------------------------
 com.google.android.youtube.tv/com.go[...].youtube.tv.activity.MainActivity#96
  rel      0 |            1 |     CLIENT |          0 |    0    0 1920 1080 |    0.0    0.0 1920.0 1080.0 |                                              [*]
---------------------------------------------------------------------------------------------------------------------------------------------------------------

可知,Youtube播放时是有一个SurfaceView来显示video画面的,我们要监测的图层也是这个SurfaceView,它的id是#99

然后要获取这个layer的完整名字

$ adb shell dumpsys SurfaceFlinger --list
Display 0 name="Built-in Screen"#3

63cfece ActivityRecordInputSink com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.MainActivity#102
ActivityRecord{eef71ef u0 com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.MainActivity#94
e145d51 com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.MainActivity#95
Background for SurfaceView[com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.MainActivity]#100
SurfaceView[com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.MainActivity]#98
SurfaceView[com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.MainActivity](BLAST)#99

最后执行dumpsys SurfaceFlinger --latency看看到底会输出什么内容:

$ adb shell dumpsys SurfaceFlinger --latency "SurfaceView[com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.MainActivity]\(BLAST\)#99"
16683294
6870189659137   6870202006496   6870189659137
6870206342431   6870218689941   6870206342431
6870223025725   6870235371015   6870223025725
6870239709019   6870252064348   6870239709019
6870256392313   6870268737385   6870256392313
6870273075607   6870285421718   6870273075607
6870289758901   6870302111163   6870289758901
6870306442195   6870318792718   6870306442195
6870323125489   6870335474904   6870323125489
6870339808783   6870352160978   6870339808783
6870356492077   6870368841496   6870356492077
6870373175371   6870385524422   6870373175371
......

一坨一坨的数字到底是啥意思呢?

  • 第一行的数字

    代表当前的VSYNC间隔,单位是纳秒。例如现在的屏幕刷新率是60Hz的,因此就是16.6ms

  • 后面 3列127行 的数字

    每一行有 3 个数字,每个数字都是时间戳,单位是纳秒,分别表示 desiredPresentTime,actualPresentTime,frameReadyTime,在计算FPS的时候,使用的是第二个时间戳。

actualPresentTime就是present fence signal的时间,根据获取的这127个actualPresentTime数据,就可以计算一秒内有多少个present fence signal了,也即多少帧刷新到屏幕了。

计算公式

更多信息阅读原文

Android Graphics 显示系统 - 计算FPS的原理与探秘Present Fence

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值