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