App/Sf的Vsync部分源码流程结合perfetto/systrace分析
千里马学框架
编辑于 2024年01月08日 07:42

hi,粉丝朋友们: 本节将使用perfetto的trace来巩固Vsync的源码分析的部分的流程。 具体抓取trace方法及相关操作建议:

a.抓取trace期间需要主要不能让画面一直刷新,因为这样一直刷新不方便看vsync的结束和开始

b.建议选着桌面,滑动桌面一下后停止1左右,再继续滑动,尽量让抓取的trace可以有如下图的间隔效果

c.需要在surfaceflinger中额外补充自己加的一些ATRACE代码方便追踪流程

1、app请求Vsync部分

这里我们回忆一下app的Vsync的申请,一般都是app主动申请的,一般都是应用端 Choreographer#scheduleVsyncLocked方法跨进程调用到Surfaceflinger端的requestNextVsync方法,具体trace如下图

应用端如下:

surfaceflinger作为服务端如下:

所有的Vsync逻辑其实就是从SurfaceFlinger的requestNextVsync方法开始的:

调用到了 mEventThread->requestNextVsync(this); 代码如下

这里都会吧vsyncRequest变变成Single,最重要mCondition.notify_all()会唤醒一直等待的EventThread的线程,让他继续执行: 这里唤醒也可以通过perfetto看出来:

对于的代码:

EventThread的执行情况如下:

这里发现主要业务在  mVSyncSource->setVSyncEnabled(true)代码中,这里面开启了一系列的vsync计算和定时任务

大概上面可以看出调用栈如下:

----》VSyncSource->setVSyncEnabled(true)

----》 VSyncDispatchTimerQueue::schedule

----------》rearmTimerSkippingUpdateFor

--------------》setTimer  这样就完成了设置定时触发操作,具体这里不进行详细讲述定时这部分,因为较为复杂需要单独章节,这里大家只要知道最后获取了自己app的vsync触发时间targetTime,定时器设置了到时间就触发,回调相关的timeCallback方法。  到此就EventThread就执行到了等待定时等待状态,等待定时时间的到来。

2、Vsync时间到了后触发timeCallback

上面步骤的定时器触发后,回调执行是在单独的TimerDispatch线程,具体trace的体现如下:

来看看timeCallback里面又干啥了,代码如下:

主要干的几件事如下:

1、遍历3个类型vsync看看谁是有定时任务的,比较wakeupTime和intentedTime符合回调情况

2、符合回调情况的,会调用executing清除定时器,和wakeupTime,并吧intentedTime设置成很大数字

3、继续下一个rearmTimer定时任务

4、回调相关类型vsync,vsync到来

上面四个任务就是TimeDispatch完成的。 这里要注意一下mCallbacks这个变量哪来的,是怎么对应刚好就是app,appSf,sf,3个vsync的呢? mCallbacks都是通过这个方法register来注册的:

那么什么地方调用注册呢?实在构造VSyncCallbackRegistration时候

那么这个VSyncCallbackRegistration是在什么时候构造的呢?

对于sf是在MessageQueue中进行的:

对于EventThread如下代码进行的:

那么上面就知晓各自的callback在哪里了。

接下来这里重点关注一下回调vsync部分的任务,app类型的vsync回调任务代码如下:

frameworks/native/services/surfaceflinger/Scheduler/DispSyncSource.cpp的CallbackRepeater的callback方法:

这里的mCallback在哪里赋值的呢?

重点看看 DispSyncSource::onVsyncCallback方法:

但是这里唤醒App的EventThread后trace中没看到具体干啥活,因为我们没有加相关ATRACE,加上一下相关TRACE如下:

明显看到这里其实就是开始派发Vsync相关的Event到app端。

callback这里除了onVsyncCallback之外,下一步就是执行新的一次schedule进行相关的定时触发Vsync任务。 代码就是上面callback方法的

这里面又回到最开的是设置定时器任务了,启动下一个vsync的定时,这里其实就可以知道,也就是app发起第一次的vsync请求,一般都会有两个vsync定时任务哈。

3、app的vsync继续请求和sf的vsync申请

针对上面分析的已经知道了第一次的app请求vsync情况,但是一般app都是会很多次的vsync连续请求,因为比较少见就一次刷新情况。那么看看产生连续的vsync请求会是什么样。

明显看到第二次的requestVsync就和 第一次不一样了,这个时候的并没有唤醒app的EventThread线程。因为前面代码就说了这个情况

也就是一旦Vsync的定时器启动后,EventThread的线程就会阻塞,知道定时器时间到了才会继续执行,后面的app的requestVsync根本不会唤醒。

那么他的唤醒靠谁呢?当然是靠第一次timeCallback时候的定时器触发啦

那么app的vsync就是这样循环只要不断有app的requestVsync,那么SurfaceFlinger的Vsync呢?

首先我们得知道sf的vsync也是由app层面进行queuebuffer后,通过跨进程setTransactionState方法调用到SurfaceFlinger端,SurfaceFlinger端会检测transation是否有相关的变化,有变化则触发申请vsync信号:

上面的调用关系都很简单,没啥业务,直到调用到了dispatch的schedule才是核心

来看看核心方法callback->schedule是怎么计算的这个wakeup等重要参数的:

核心方法是nextAnticipatedVSyncTimeFrom,这个方法很复杂,大家先把他当做个功能黑盒,后面会带大家详细分析。

所以Sf接收到跨进程transaction的请求后申请vsync,计算出来的vsync时间,从而得出wakeupTime,一般这里的wakeupTime会大于前面app Vsync时候定时的wakeupTime,所以这里不会进行任何的定时。 结合trace看看vsync的情况: sf的vsync申请触发部分情况:

和app vsync一起合作的解释部分:

这里展开一下app ,sf都被回调的trace,对应的代码就是上面的timeCallback方法:

4、app vsync结束部分

vsync有申请就肯定有结束,不可能app没有申请情况下,vsync还一直不断的运行,这样对于系统的功耗影响巨大,而且也是无用功,所以vsync坚持的原则就是有需要用时候app主动申请,不需要了就停止。 上面只分析了vsync怎么开始的,接下来分析vsync的结束部分逻辑

逻辑是如下情况:

关闭定时器步骤trace:

该部分对应核心代码回顾: app的EventThread执行时候,会把vsyncRequest变成0,代表没有app的vsync请求了

总结如下:

1、EventThread最核心的vsyncRequest是app进行requestVsync进行改变成Single

2、vsync定时时间到会回调触发EventThread加入个event,event就是来派发Vsync事件给具体的connection即app,通过socket方式

3、消费了event同时,需要吧前面app设置的vsyncRequest变成SingleSuppressCallback

4、下次如果vsync再次触发,但是没见到新的app吧vsyncRequest变成Single,如果还是上次的SingleSuppressCallback,那么vsyncRequest就变成了None,变成None之后就会调用   mVSyncSource->setVSyncEnabled(false)关闭vsync

本文章对应视频手把手教你学framework: hal+perfetto+surfaceflinger网页链接​

私聊作者+v(androidframework007)

七件套专题:

点击这里 网页链接​

视频:网页链接​