android15 vsync源码分析

一 概要

整个vsync可以分为4个角色:

APP:通过gui::DisplayEventReceiver与服务端建立连接,请求和接收vsync信号,用于驱动app侧绘制。

SurfaceFlinger:安卓重要角色之一,在这里有3个重要功能:(1)初始化和管理vsyn模块;(2)向app侧提供vsync接口;(3)获取自身上屏用的vsync信号

Scheduler:vsync管理模块对外的接口。负责vsync预测、分发等核心功能。

hardware vsync:硬件vysnc信号原始来源。

二 四角色一次拆解

2.1 APP

gui::DisplayEventReceiver创建和持有EventThreadConnection对象,通过该对象请求和接收vsync信号,APP侧如何基于gui::DisplayEventReceiver进行更多层次的封装,给上层提供更友好的接口,不在本文讨论范围。这里有个小技术点:BitTube,用于接受vsync事件的对象,BitTube是对socket的封装,为什么不用回调,或者广播的形式呢?socket更高效,不阻塞vsync事件分发。

2.2 SurfaceFlinger(简称sf)

在vysnc模块中,sf负责与Scheduler模块协作,初始化Vsync模块,注册sf和app vsync事件回调,向vsync模块添加present fence时间戳和硬件vsync原始时间戳,用于预测模块构建。

2.3 Scheduler

理解该模块,有几个核心概念。

预测模型计算:根据硬件vsync时间戳建立标准预测模型,通过present fence时间戳更新模型,并当误差大于一定值后,开启硬件vsync采样,重建标准预测模型。在采样时间戳时,有各种逻辑需要处理,如可变分辨率vrr,渲染率render rate,显示模式display mode等改变时,period如何管理。

schedule:当vsync请求发起时,如何调度。有多个请求者时,如何综合设置定时器。schedule()/update()/pendingWorkloadUpdate()三者在什么情况下使用?计算好的nextVsyncTime为什么还需要adjust?

回调注册和响应:通过MessageQueue和EventThread注册回调。关注初始化时机,使用场景。MessageQueue为什么可以直接被分发,EventThread为什么需要维护一个线程异步分发?

nextVsyncTime预测:两个核心问题:nextVsyncTime预测和修正。预测是很简单的,根据之前构建的预测模型Model(slope, intercept),直接计算next vsync timestamp。为什么需要修正呢?由于可变分辨率vrr,显示模型display mode切换等原因,需要对next vsync timestamp进行修正。

2.4 Hardware vsync

硬件vsync,吐出最原始的、标准的vsync信号,为什么不直接用vsync信号,非要搞出这么麻烦的vsync软件模块呢?核心原因是功耗和性能问题,硬件vsync信号频率太高,从内核态传到用户态,有大量的开销。另外也可能处于对vsync更好的管理和优化,减少上屏延迟。

至于SurfaceFlinger如何拿到Hardware vsync,有个专门的一个模块,叫HWComposer,不在本文讨论范围,简单的理解,通过一个callback事件,可以拿到硬件vsync事件。

三 细节拆解

3.1 三大流程

(1) 核心对象创建和callback绑定

重点看EventThread回调和MessageQueue回调注册时机

(2)sf申请vsync

重点看nextVsyncTime在什么时候计算,定时器如何设置,callback如何分发

一个技术点:epoll实现的Timer,为什么不直接用thread sleep?

(3)app申请vsync

重点看threadMain,为什么需要pendingEvents, connection建立时机,如何分发等。

需要关注的一个技术点:BitTube,socket建立双向连接,高效、高频收发数据。

3.2 timestamp管理

几个核心模块:

预测模型计算:简单线性回归计算斜率和截距,得到模型Model(slope, intercept),发生在VsyncPredictor。

PresentFence采样:把present fence上屏时间戳,用于模型调整,如果误差较大,则开启硬件采样。这里需要处理FenceTime还没及时唤醒的情况

onComposerHalVsync:标准硬件vsync时间戳,正常情况下,硬件时间戳直接收集并计算预测模型,但当display mode变化时,需要管理period,比较麻烦(这块本文没有细化,待补充)。

DisplayMode变化时period管理:待补充

VsyncModulator:待补充

3.3 schedule流程

三个重要逻辑:立即更新逻辑(workloadUpdate);schedule逻辑,update逻辑

大逻辑非常简单:如果now很大,则说明有负载瓶颈,依赖最近timer唤醒,本次无需改变定时器;否则预测当前请求者(callback→schedule()),update每一个callback(除当前请求者外),然后取最小唤醒时间,重新设定定时器

  • adjustVsyncIfNeeded()拆解

nextVsyncTime与mLastDispatchTime相比,上下波动mMinVsyncDistance(3ms),则说明已经分发过,则向mMinVsyncDistance后再预测一个

nextVsyncTime与mLastDispatchTime相比少于currentPeriod,则说明too close,则向+currentPeriod后,再预测一个

nextVsyncTime否则,直接使用

  • getExpectedCallbackTime()拆解

nextVsyncTime直接使用,不折腾

  • getArmedInfo(tracker, now, timing)拆解

now + timing.workDuration + timing.readDuration依此向后预测

  • mIntendedWakeupTime

初始化时,被设置为max()

timeCallback()被唤醒时,被重置为max()

cancel(CallbackToken)/cancelTimer()时,被重置为max()

setTimer(nsecs_t targetTime)时,被设置为有效值

  • mArmedInfo

schedule() / update()时,会赋有效值

timeCallback()满足唤醒条件时,被reset()

3.4 nextAnticaptedVsyncFrom下一个vsync计算和修正

根据预测模型Model(slope, intercept)计算vsync是非常简单的,由于vrr可变渲染率和多个渲染配置,导致vsync计算非常复杂,这里介绍主要流程和几个重要函数。

  • std::unordered_map<nsecs_t, Mode> mRateMap

存储ideal period对应的预测模型Model(slope, intercept),在addVsyncTimestamp()时赋值。

Map between ideal vsync period and the calculated model

  • snapToVsync(nsecs_t timePoint)拆解

根据计算好的预算模型Model(slope, intercept),计算timePoint对应的预测值

  • nextAnticipatedVSyncTimeFrom()拆解
  • 预测的vsync,相对于render state做适当修正:snapToVsyncAlignedWithRenderState()
  • 如果开启vrr(动态渲染率),则做适当修正
  • mTimelines拆解

render rate渲染率发生变化时,cache参数,用于修正预测值

  • purgeTimelines()拆解

清空过时的timelines

  • vrr模式下相关修正

onFrameBegin() / onFrameMissed()

ensureMinFrameDurationIsKept()

本质:vrr模式下,显示模式改变,则导致period变化,相关计算参数重置,需要重新计算

相关函数:setRenderState() / setDisplayModePtr(); onFrameBegin() / onFrameMissed()

ensureMinFrameDurationIsKept()

  • todo vrr/render rate修正细节逻辑
相关推荐
阿巴斯甜19 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker20 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952721 小时前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android