首页 > 其他分享 >Flutter帧率监控 | 由浅入深,详解获取帧率的那些事

Flutter帧率监控 | 由浅入深,详解获取帧率的那些事

时间:2023-02-21 17:10:49浏览次数:51  
标签:由浅入深 16.6 List FrameTiming 获取 详解 ui 绘制 Flutter

前言

做线上帧率监控上报时,少不了需要弄明白如何通过代码获取实时帧率的需求,这篇文章通过图解配合Flutter性能调试工具的方式一步步通俗易懂地让你明白获取帧率的基础知识,以后再也不愁看不懂调试工具上指标了。

说说 List<FrameTiming>

Flutter 中通过如下方式监听帧率,addTimingsCallback 涉及到帧调度知识,感兴趣可以看看这篇​​Flutter 帧调度过程​​。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_跨平台

这里重点说说 List<FrameTiming>。

List<FrameTiming>从哪里来

addTimingsCallback 定义:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_android_02

List<FrameTiming>可简单理解成:引擎层到框架层的帧数据流。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_跨平台_03

List<FrameTiming>何时有值

List<FrameTiming>则表示一系列实时帧信息。

如点击屏幕按钮,引擎将传递系列帧信息到框架层:“框架层,屏幕发送了变化,准备回调数据更新了!”。如果用户未操作,addTimesCallback 则不会回调。

因此 ,addTimesCallback(List<FrameTiming>)只有用户操作界面时参数才有值

List<FrameTiming>中帧存储顺序

List<FrameTiming>中 0 的位置是第一帧,last 是最新一帧。 最新的帧永远在最后面

再说说 FrameTiming

通过这个单词不难猜测 Frame 表示帧,加上 Timing 可以理解成实时变化的帧。FrameTiming 是一个用来存储实时帧信息的数据结构

FrameTiming 定义:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_跨平台_04

这里列了下我认为最重要的几个属性:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_flutter_05

前置知识简单说明

理解上述属性前需了解渲染相关知识,不清楚的可以看看​​Vsync 机制​​​ 和 ​​卡顿产生原因​​ 。

核心思想
图像内容展示到屏幕的过程需要 CPU 和 GPU 共同参与。CPU 负责计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。之后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。

FrameTiming 在帧中的表示

当在应用中操作时候,就会产生连续的帧,如图:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_跨平台_06

每两个柱形一起表示一帧:ui 表示 cpu 耗时,raster 表示 gpu 耗时。

每帧细化后如下图,其中标注 ①②③④ 对应 FrameTiming 中的四个主要属性。而其中:

  • ui 在 FrameTiming 中有对应衍生变量叫 buildDuration 。
  • Raster 在 FrameTiming 中用 RasterDuration 表示。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_帧率_07

同时可推导出 FrameTiming 中相关衍生变量与上述重点关注属性关系:

④-① = totalSpan:同步信号开始到栅格化时间

②-① = vsyncOverhead:同步信号接受后到 ui 构建之间延迟。

③-② = buildDuration:ui 构建过程总时间。

④-③ = rasterDuration:栅格化过程总时间。

totalSpan 与 buildDuration+rasterDuration 关系

通过代码验证 Flutter 调试工具 PerformanceOverlay 中 Timing 每帧 ui 值和 ration 值与 vsyncstart、buildstart、buildFinish、rasterStart、rasterFinish 关系。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_前端_08

输出:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_前端_09

代码中,11 行是 ui 构建 + 栅格化时间,17 行是 totalSpan 时间, 22 行中是 vsyncOverhead + ui 构建 + 栅格化时间 这个值最终和才等于 totalSpan 值。

这里有个误区, 网上很少人关注 totalSpan 与 buildDuration+rasterDuration 关系,好像默认就是相等的。其实,totalSpan 不等于 Timing 中 ui + raster 值而是 Vsync 信号接受后构建之前延迟 vsyncOverhead+cpu 构建耗时 + gpu 耗时

通过上述案例和 totalSpan 定义很容易佐证这点:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_flutter_10

如何获取帧率

核心思路

  1. 将原始帧数据 List降噪保留最新关注帧数。
  2. 通过公式 FPS≈ REFRESH_RATE * 实际绘制帧数 / 理论绘制帧数 。

如何降噪

  • 从原生数据中筛查最新关注帧数,其他都干掉。
    如下,通过栈方式调换了存储方式更容易操作,然后将栈中老的干掉只保留最新的关注 100 条。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_android_11

  • 将位于不同帧的无效数据过滤掉。
    如下,以刷新率为 60 举例,如果一帧之间的时间 > 16.6 *2,该帧就位于不同帧中,因为一帧最大时间也就是 16.6ms。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_跨平台_12

如何计算

代码如下:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_前端_13

这里拆解下其中逻辑,方便理解。

有 5 帧,其中在实际绘制过程中 f① 和 f② 都是在正常时间范围内绘制,f③ 则会绘制耗时,跨越 2 帧。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_前端_14

假设 f①,f②,f③ 绘制总耗时为 P1, P2, P3 则:

  1. 理论绘制帧数 = (P1 / 16.6)+ 1 + (P2 / 16.6) + 1 + (P3 / 16.6) + 1 图中明显可以看到 P1 和 P2 < 16.6, 而 P3 > 16.6 *2 ,所有理论绘制帧数 = 0 +1 + 0 + 1 + 2 + 1 = 5。
  2. 实际绘制帧数 = 3 。
  3. 本来正常应该绘制 5 帧,但是实际绘制 3 帧,取比值表示实际绘制能力,根据 FPS≈ REFRESH*RATE * 实际绘制帧数 / 理论绘制帧数 。 即 FPS = 3 _ 60 / 5。

完整代码

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_前端_15

效果展示

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_前端_16

这就结束了?

上面代码在刷新率为 60HZ 的手机上每秒绘制帧时间为 16.6 是没有问题的,但是如果在其他帧率的手机上,比如 90HZ(OnePlus 7 Pro), 120HZ(Redmi K30)上就会存在问题。

  1. 代码中写死了 REFRESH_RATE = 60 。
  2. maxframes = 100 也有问题,如果在 60HZ 手机上取 100 帧绰绰有余,在 120HZ 手机上的话,每秒绘制 120 帧显然不够。

如何获取帧率(改进版)

思路:通过通道获取各系统提供的刷新率获取方式,然后更新上述代码中的刷新率。

获取各系统帧率

在 Android 和 ios 平台提供了获取帧率的方法。

  • 对于 Android 通过 WindowManager 获取刷新率:

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_android_17

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_flutter_18

定义统一获取接口并实现(以安卓为例)

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_flutter_19

定义接口

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_帧率_20

最终修改点

  1. 最大帧率数修改成 120。
  2. fpsHZ 这个值通过插件动态获取。
  3. 时间间隔也同步修改下,也就是 16.6(60hz 的时候)。
  4. 最后 fps 计算公式中的刷新率同步修改成 fpsHZ。

Flutter帧率监控  | 由浅入深,详解获取帧率的那些事_前端_21

总结

本文重点讲解了 FrameTiming 结构在帧显示过程中的对应关系,图解获取准确帧的算法,最后完善了获取帧的逻辑。

总体来说网上能搜到的我这里都有,在学习过程中遇到 FrameTiming 结构和帧率计算方法这两个点觉得不好理解,不够系统,就重点介绍争取深入浅出表达出来。不足之处还望各位大佬指出,谢谢!

如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是我创作最大的动力。

❤️ 本文原创​​听蝉​​ 公众号:码里特别有禅 欢迎关注原创技术文章第一时间推送 ❤️

PS: 文中所有源码获取方式:公众号后台回复 “fps”

参考链接

​如何代码获取 Flutter APP 的 FPS - Yrom's​

​Flutter 如何更加准确地获取 FPS | 区长​

​Flutter 性能计算之流畅性 fps 计算 - 简书​

​allenymt/flutter_fps: flutter Fps 的两种监听方案​

如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是我创作最大的动力。

❤️ 本文原创​​听蝉​​ 公众号:码里特别有禅 欢迎关注原创技术文章第一时间推送 ❤️



标签:由浅入深,16.6,List,FrameTiming,获取,详解,ui,绘制,Flutter
From: https://blog.51cto.com/u_15274598/6076759

相关文章

  • Flutter异常监控 - 叁 | 从bugsnag源码学习如何追溯异常产生路径
    如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是我创作最大的动力。❤️本文原创​​听蝉​​公众号:码里特别有禅欢迎关注原创技术文章第一时间推......
  • Flutter异常监控 - 肆 | Rollbar源码赏析
    一.Rollbar可以帮你解决哪些问题无特别说明,文中Rollbar统指​​Rollbar-flutter​​1.代码复用Rollbar​​官方文档​​说是纯Dart实现,该特征意味着自带”代码复用”光环......
  • Flutter异常监控 - 壹 | 从Zone说起
    如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是我创作最大的动力。❤️本文原创​​听蝉​​公众号:码里特别有禅欢迎关注原创技术文章第一时间推......
  • 一文详解SpEL表达式注入漏洞
    摘要:本文介绍了SpEL表达式以及常见的SpEL注入攻击,详细地介绍了部分漏洞攻击实例以及常用的漏洞检测与防御手段。本文分享自华为云社区《​​SpEL表达式注入漏洞分析、检查与......
  • 华为HCIA认证R&S路由与交换综合实验案例详解
    HCIA-R&S综合实验一这篇文章主要介绍了华为HCIA认证R&S路由与交换综合实验,结合具体实验案例形式详细分析了华为HCIA认证路由与交换子网划分、路由配置相关原理、操作技巧与......
  • 使用java.util.Timer实现定时任务,详解Thread.sleep() in a loop, probably busy-waiti
    很多时候,我们需要定时任务实现一些诸如刷新,心跳,保活等功能。这些定时任务往往逻辑很简单,使用定时任务的框架(例如springboot@Scheduled)往往大材小用。下面是一个定时任......
  • 一文详解SpEL表达式注入漏洞
    摘要:本文介绍了SpEL表达式以及常见的SpEL注入攻击,详细地介绍了部分漏洞攻击实例以及常用的漏洞检测与防御手段。本文分享自华为云社区《SpEL表达式注入漏洞分析、检查与防......
  • 一、全国医保接口开发详解(整体介绍)
    一、开发过程1、需求分析第一、首先肯定要仔细阅读接口文档,设计接口系统整体架构,也就是接口系统、HIS系统、医保系统各自的职责。搞清楚文档接口要实现的技术,是调用程......
  • bert 的输出格式详解
    输出是一个元组类型的数据,包含四部分,lasthiddenstateshape是(batch_size,sequence_length,hidden_size),hidden_size=768,它是模型最后一层的隐藏状态pooler_output......
  • K8SYaml文件详解(云原生)
    一、K8S支持的文件格式kubernetes支持YAML和JSON文件格式管理资源对象。JSON格式:主要用于api接口之间消息的传递YAML格式:用于配置和管理,YAML是一种简洁的非标记性语言,内......