Android Vsync 原理

2022-05-13 20:01:49 浏览数 (1)

我们知道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再重新计算.

0 人点赞