前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android Vsync 原理

Android Vsync 原理

作者头像
用户9732312
发布2022-05-13 20:01:49
1.6K0
发布2022-05-13 20:01:49
举报
文章被收录于专栏:ADAS性能优化

我们知道Android是用Vsync来驱动系统的画面更新包括APPview draw ,surfaceflinger 画面的合成,display把surfaceflinger合成的画面呈现在LCD上.我们将在本文探讨Android的Vsync的实现.

Vsync的构成

在systrace中,我们经常可以看到如上图的信息.

  • 红色框1是与Vsync相关event信息.这些Vsync event构成了android系统画面更新基础.
  • 红色框2和红色框3是Vsync-app的信息.我们可以看到红色框2比红色框3稀疏.我们将会在本文说明其原因.
  • 红色框4和红色框5是Vsync-sf的信息.我们可以看到红色框4比红色框5稀疏.我们将会在本文说明其原因.

Vsync 信号

Vsync信号由HW_VSYNC_ON_0,HW_VSYNC_0, Vsync-app和Vsync-sf四部分构成.

  • HW_VSYNC_ON_0 代表PrimaryDisplay的VSync被enable或disable.0这个数字代表的是display的编号, 0是PrimaryDisplay,如果是Externalmonitor,就会是HW_VSYNC_ON_1.当SF要求HWComposer将Display的VSync打开或关掉时,这个event就会记录下来.
  • HW_VSYNC_0 代表PrimaryDisplay的VSync发生时间点, 0同样代表display编号.其用来调节Vsync-app和Vsync-sfevent的输出.
  • Vsync-app App,SystemUI和systemserver 等viewdraw的触发器.
  • Vsync-sf Surfaceflinger合成画面的触发器.

通常为了避免Tearing的现象, 画面更新(Flip)的动作通常会在VSync开始的时候才做, 因为在VSync开始到它结束前, Display不会把framebuffer资料显示在display上, 所以在这段时间做Flip可以避免使用者同时看到前后两个部份画面的现象.目前user看到画面呈现的过程是这样的,app更新它的画面后,它需要透过BufferQueue通知SF, SF再将更新过的app画面与其它的App或SystemUI组合后, 再显示在User面前. 在这个过程里, 有3个component牵涉进来, 分别是App,SF, 与Display.以目前AndroidM的设计,这三个Component都是在VSync发生的时候才开始做事.我们将它们想成一个有3个stage的pipeline,这个pipeline的clock就LCD的TE 信号(60HZ)也即HW_VSYNC_0.

我们来看看android draw的pipeline.如下,

1. T = 0时, App正在画N, SF与Display都没内容可用

2. T = 1时, App正在画N+1, SF组合N, Display没Buffer可显示

3. T = 2时, App正在画N+2, SF组合N+1, Display显示N

4. T = 3时, App正在画N, SF组合N+2, Display显示N+1

5. ...

如果按照这个步骤, 当user改变一个画面时, 要等到2个VSync后, 画面才会显示在user面前, latency大概是33ms (2个frame的时间). 但是对大部份的操作来讲, 可能app加SF画的时间一个frame(16.6ms)就可以完成. 因此, Android就从HW_VSYNC_0中产生出两个VSync 信号,VSYNC-app是给App用的, VSYNC-sf是给SF用的, Display则是使用HW_VSYNC_0.VSYNC-app与VSYNC-sf可以分别给定一个phase, 简单的说

VSYNC-app = HW_VSYNC_0 + phase_app

VSYNC-sf =HW_VSYNC_0 + phase_sf

从而使App draw 和surfaceflinger的合成,错开时间运行.这样就有可能整个系统draw的pipeline 更加有效率,从而提高用户体验.

也就是说, 如果phase_app与phase_sf设定的好的话, 可能大部份user使用的状况, App+SF可以在一个frame里完成, 然后在下一个HW_VSYNC_0来的时候, 显示在display上.

理论上透过VSYNC-sf与VSYNC-app的确是可以有机会在一个frame里把App+SF做完, 但是实际上不是一定可以在一个frame里完成. 因为有可能因为CPU调度,GPUperformance 不够, 以致App或SF没办法及时做完. 但是即便如此,把app,surfaceflinger和displayVsync 分开也比用一个Vsync 来trigger appdraw,surfaceflinger合成,display 在LCD 上draw的性能要好很多.(FPS 更高).

那么是否我们收到LCD的Vsyncevent 就会触发Vsync-app和Vsync-SF呢.如果是这样我们就不会看到本文开头的Vsync-app和Vsync-SF的节奏不一致的情况(红色框2比红色框3稀疏).事实上, 在大多数情况下,APP的画面并不需要一直更新.比如我们看一篇文章,大部份时间,画面是维持一样的,如果我们在每个LCD的VSYNC来的时候都触发SF或APP, 就会浪费时间和Power.可是在画面持续更新的情况下,我们又需要触发SF和App的Vsync event.例如玩game或播影片时.就是说我们需要根据是否有内容的更新来选择Vsyncevent出现的机制.Vsync event 的触发需要按需出现. 所以在Android里, HW_VSYNC_0,Vsync-sf和Vsync-app都会按需出现. HW_VSYNC_0是根据是否需要调节sf和app的Vsyncevent而出现,而SF和App则会call requestNextVsync()来告诉系统我要在下一个VSYNC需要被trigger.也是虽然系统每秒有60个HW VSYNC,但不代表APP和SF在每个VSYNC都要更新画面. 因此, 在Android里,是根据SoftwareVSYNC(Vsync-sf,Vsync-app)来更新画面.Software VSYNC是根据HWVSYNC过去发生的时间,推测未来会发生的时间.因此,当APP或SF利用requestNextVsync时,Software VSYNC才会触发VSYNC-sf或VSYNC-app.

Vsync event的产生

下图是Vsyncevent 产生的示意图.

这里HW_VSYNC就是HW_VSYNC_0.

当SF从HWComposer收到VSYNC(HW_VSYNC_0)时, 它会利用DispSync::addResyncSample将新的VSYNC时间交给DispSync.addResyncSample决定是否还需要HW_VSYNC的输入, 如果不需要, 就会将HW_VSYNC关掉.

voidSurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {

bool needsHwVsync = false;

{ // Scope for the lock

Mutex::Autolock _l(mHWVsyncLock);

if (type == 0 &&mPrimaryHWVsyncEnabled) {

needsHwVsync =mPrimaryDispSync.addResyncSample(timestamp);

}

}

if (needsHwVsync) {

enableHardwareVsync();

} else {

disableHardwareVsync(false);

}

}

另一方面,在sufaceflinge合成图片后也会check是否需要开启HW_VSYNC来调整SW_VSYNC.

在SurfaceFlinger::postComposition()里, 会将PresentFence的时间通过addPresentFence交给DispSync,来检查SW_VSYNC是否需要校正, 如果需要, 就会将HW_VSYNC打开.

voidSurfaceFlinger::postComposition()

{

const LayerVector&layers(mDrawingState.layersSortedByZ);

const size_t count = layers.size();

for (size_t i=0 ; i<count ; i++) {

layers[i]->onPostComposition();

}

const HWComposer& hwc =getHwComposer();

sp<Fence> presentFence =hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);

if (presentFence->isValid()) {

if(mPrimaryDispSync.addPresentFence(presentFence)) {

enableHardwareVsync();

} else {

disableHardwareVsync(false);

}

}

const sp<const DisplayDevice>hw(getDefaultDisplayDevice());

if (kIgnorePresentFences) {

if (hw->isDisplayOn()) {

enableHardwareVsync();

}

}

…..

}

DispSync是利用HW_VSYNC和PresentFence来判断是否需要开启HW_VSYNC.HW_VSYNC 最少要3个, 最多是32个, 实际上要用几个则不一定,DispSync拿到3个HW_VSYNC后就会计算出SW_VSYNC,只要收到的PresentFence没有超过误差,则HW_VSYNC就会关掉,以便节省功耗. 不然会继续开启HW_VSYNC计算SW_VSYNC的值, 直到误差小于threshold.其计算的方法是DispSync::updateModelLocked().

基本思想如下,

  • 计算目前收到HW_VSYNC间隔, 取平均值(AvgPeriod) HW_VSYNC
  • 将每个收到的VSYNC时间与AvgPeriod算出误差. (Delta = Time %AvgPeriod)
  • 将Delta转换成角度(DeltaPhase), 如果AvgPeriod是360度,DeltaPhase = 2*PI*Delta/AvgPeriod.
  • 从DeltaPhase可以得到DeltaX与DeltaY (DeltaX =cos(DeltaPhase), DeltaY = sin(DeltaPhase))
  • 将每个收到的VSYNC的DeltaX与DeltaY取平均, 可以得到AvgX与AvgY
  • 利用atan与AvgX, AvgY可以得到平圴的phase (AvgPhase)
  • AvgPeriod + AvgPhase就是SW_VSYNC.
  • 当DispSync收到addPresentFence时(最多记录8个sample),每一个fence的时间算出(Time% AvgPeriod)的平方当作误差,将所有的Fence误差加总起来如果大于某个Threshold,就表示需要校正(DispSync::updateErrorLocked). 校正的方法是呼叫DispSync::beginResync()将所有的HW_VSYNC清掉, 开启HW_VSYNC.等至少3个HW_VSYNC再重新计算.
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android性能优化 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Vsync的构成
    • Vsync 信号
      • Vsync event的产生
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档