我们知道Android是用Vsync来驱动系统的画面更新包括APPview draw ,surfaceflinger 画面的合成,display把surfaceflinger合成的画面呈现在LCD上.我们将在本文探讨Android的Vsync的实现.
在systrace中,我们经常可以看到如上图的信息.
红色框1是与Vsync相关event信息.这些Vsync event构成了android系统画面更新基础.
红色框2和红色框3是Vsync-app的信息.我们可以看到红色框2比红色框3稀疏.我们将会在本文说明其原因.
红色框4和红色框5是Vsync-sf的信息.我们可以看到红色框4比红色框5稀疏.我们将会在本文说明其原因.
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.
下图是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
layers[i]->onPostComposition();
}
const HWComposer& hwc =getHwComposer();
sp
if (presentFence->isValid()) {
if(mPrimaryDispSync.addPresentFence(presentFence)) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
const sp
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再重新计算.
我们知道Vsync是androiddisplay系统的重要基石,其驱动androiddisplay系统不断的更新App侧的绘画,并把相关内容及时的更新到LCD上.其包含的主要代码如下:
frameworks\native\services\surfaceflinger\DispSync.cpp
frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp
frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp@DispSyncSource
frameworks\native\services\surfaceflinger\EventThread.cpp
frameworks\native\services\surfaceflinger\MessageQueue.cpp
frameworks\native\libs\gui\BitTube.cpp
frameworks\native\libs\gui\BufferQueueProducer.cpp
DispSync.cpp
这个class包含了DispSyncThread.是SW-SYNC的心脏.所有的SW-SYNCevent均由其产生.在Android系统中,只有一个DispSync.
SurfaceFlinger.cpp
这个类主要处理layer的合成.它合成好相关的layer后发送command给HWdisplay进行进行显示.
DispSyncSource
Vsync source 在Android系统中有两个instance.一是Vsync-app.另一个Vsync-sf.当SW-SYNC发生时,Vsyncsource会callback到其相应的EventThread,并且会在Systrace上显示出Vsync-sf和Vsync-app的跳变.
EventThread.cpp
Vsync event处理线程.在系统中有两个EventThread.一个用于Vsync-app,另一个用于Vsync-sf.其记录App和SurfaceFlinger的requestnextVsync()请求,当SW-VSYNCevent触发时,EventThread线程通过BitTube通知App或SurfaceFlinger.App开始drawview,SurfaceFlinger 开始合成 dirty layer.
MessageQueue.cpp
MessageQueue 主要处理surfaceflinger的Vsync请求和发生Vsync事件给surfaceFlinger.
BitTube.cpp
Vsync 事件的传输通道.App或Surfaceflinger首先与EventThread建立DisplayEventConnection(EventThread::Connection::Connection,Connection 是BnDisplayEventConnection子类,BitTube是其dataChannel).App或surfaceFlinger通过call DisplayEventConnection::requestNextVsync()(binder 通信)向EventThread请求Vsyncevent.当EventThread收到SW-VSYNCevent时,其通过BitTube把Vsyncevnet发送给App或SufaceFlinger.
BufferQueueProducer.cpp
在Vsync架构中,其主要作用是向EventThread请求Vsync-sfevent.当App画完一个frame后,其会把画完的buffer放到bufferqueue中通过call BufferQueueProducer::queueBuffer().进而surfaceflinger进程会通过callDisplayEventConnection:: requestNextVsync()向EventThread请求Vsync event.
App需要draw一个frame时,其会向EventThread(appEventThread)请求Vysncevent,当EventThread收到Vsyncevent时,EventThread通过BitTueb把Vsyncevent通知到App,同时跳变systrace中的Vsync-app.App收到Vsync-app,开始draw frame.当App画完一个frame后,把画好的内容放到bufferqueue中,就会要求一个Vsync-sfevent,以便surfaceflinger在收到Vsync-sfevent时合成相关的dirty的内容并通知DisplayHW.Display HW 会在下一个HWvsync时间点,把相关的内容更新到LCD上完成内容的更新.
下面是其大概的流程图,
(SF-1)APP 画完一个frame以后,就会把其绘画的buffer放到buffer queue中.从生产者和消费者的关系来看,App是生产者,surfaceflinger进程是消费者.
(SF-2)在SurfaceFlinger 进程内,当收到有更新的可用的frame时(需要render到LCD ),就会向EventThread请求Vsync event(requestNextVsync()).EventThread会记录下此请求,当下次Vsync event到来时,便会triggerVsync-sf event.
DispSync 不断产生SW-VSYNCEVENT.当SW-VSYNCEVENT产生时,会检查是否有EventListener关心该event(SF和APP请求Vsync 时会在DispSync中创建EventListener并把其相应的DispSyncSource作为EventListener的callback),如有则调用EventListenercallback(DispSyncSource)的onDispSyncEvent.(callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime)).
DispSyncSource会引起Vsync-sf或Vsync-app跳变,当onDispSyncEvent()被调用时,并且会直接调用EventThread的onVSyncEvent().
EventThread会把Vsync-sf或Vsync-app event通知到App或surfaceFlinger当Vsync-sfevent产生时(callonDispSyncEvent),surfaceflinger进程合成dirty layer的内容(SF-5)并通知Display HW把相关的更新到LCD上.App则在Vsync-ap时开始drawview.
Display HW 便会在HW 的vsync 到来时,更新LCD 的内容(SF-6).
如果有Layer 的内容需要更新,surfaceflinger 便会把相关内容合成在一起,并且通知DisplayHW ,有相关的更新内容.
我们来看看其代码的实现.
void SurfaceFlinger::init()
{
…
// start the EventThread
sp
vsyncPhaseOffsetNs, true,"app");
mEventThread = newEventThread(vsyncSrc);
sp
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread= new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);
}
当surfaceFlinger初始化时,其创建了两个DispSyncSource和两个EventThread.一个用于APP,而另一个用于SurfaceFlinger本身.我们知道DispSyncSource和DispSync协同工作,共同完成Vsyncevent的fire.而EventThread会triggerApp draw frame和surfaceFlinger合成”dirty”layer当SW-VSYNCevent产生时.
我们在这里列出DispSyncSource和Vsync-sf有关系的code.
class DispSyncSource : publicVSyncSource, private DispSync::Callback {
public:
DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
…
virtual void setVSyncEnabled(bool enable) {
Mutex::Autolock lock(mVsyncMutex);
if (enable) {
status_t err =mDispSync->addEventListener(mPhaseOffset,
static_cast
….
} else {
status_t err = mDispSync->removeEventListener(
static_cast
….
}
virtual void onDispSyncEvent(nsecs_t when) {
sp
{
Mutex::Autolocklock(mCallbackMutex);
callback = mCallback;
if (mTraceVsync) {
mValue = (mValue + 1) % 2;
ATRACE_INT(mVsyncEventLabel.string(), mValue);
}
}
if (callback != NULL) {
callback->onVSyncEvent(when);
}
}
….
}
其中最重要的两个function是setVSyncEnabled(bool enable)和onDispSyncEvent(nsecs_twhen). setVSyncEnabled()的功能是把自己注册到DispSync(enable=true)中或者从DispSync中remove掉自己(enale=false),当DispSync产生了SW-VSYNCevent时,会check对SW-VSYNC感兴趣的DispSyncSource并回调其onDispSyncEvent().onDispSyncEvent()主要的功能一是enableVsync-sf在sytrace中,一是callback EventThread中的onVSyncEvent(),从而触发SurfaceFlinger合成所有更新的layer.
下面我们来看看EventThread的重要code
…
EventThread::Connection::Connection(
constsp
: count(-1),mEventThread(eventThread), mChannel(new BitTube())
{
}
sp
return new Connection(const_cast
}
status_tEventThread::registerDisplayEventConnection(
constsp
Mutex::Autolock _l(mLock);
mDisplayEventConnections.add(connection);
…
}
voidEventThread::requestNextVsync(
…
if (connection->count < 0) {
connection->count = 0;
mCondition.broadcast();
}
}
void EventThread::onVSyncEvent(nsecs_t timestamp){
Mutex::Autolock _l(mLock);
mCondition.broadcast();
}
bool EventThread::threadLoop() {
DisplayEventReceiver::Event event;
Vector< sp
signalConnections =waitForEvent(&event);
// dispatch events to listeners...
const size_t count= signalConnections.size();
for (size_t i=0 ; i
const sp
// now see if we still need to reportthis event
status_t err = conn->postEvent(event);
…
}
// This will return when (1) avsync event has been received, and (2) there was
// at least one connectioninterested in receiving it when we started waiting.
Vector
DisplayEventReceiver::Event* event)
{
Mutex::Autolock _l(mLock);
Vector< sp
do {
….
// find out connections waiting forevents
size_t count = mDisplayEventConnections.size();
for (size_t i=0 ; i
….
if (connection->count >= 0) {
// we need vsync eventsbecause at least
// one connection iswaiting for it
waitForVSync = true;
if (timestamp) {
if(connection->count == 0) {
// fired this timearound
connection->count = -1;
signalConnections.add(connection);
}
// Here we figure out if we need toenable or disable vsyncs
if (timestamp && !waitForVSync){
// we received a VSYNC but we haveno clients
// don't report it, and disableVSYNC events
disableVSyncLocked();
} else if (!timestamp &&waitForVSync) {
// we have at least one client, sowe want vsync enabled
// (TODO: this function is calledright after we finish
// notifying clients of a vsync, sothis call will be made
// at the vsync rate, e.g.60fps. If we can accurately
// track the current state we couldavoid making this call
// so often.)
enableVSyncLocked();
}
if (!timestamp &&!eventPending) {
// wait for something to happen
if (waitForVSync) {
…
if (mCondition.waitRelative(mLock, timeout) ==TIMED_OUT) {
…..
}
} else {
// Nobody is interested invsync, so we just want to sleep.
mCondition.wait(mLock);
}
}
} while (signalConnections.isEmpty());
…
return signalConnections;
}
我们列出了EventThread的重要function.下面一一说明其功能.
EventThread::Connection::Connection()
Connection的构造函数.用于进程间的通信by BitTube..在此处主要是搭建一个通路(BitTube)来完成client(App或SurfaceFlinger)对Vsyncevent事件的请求(通过requestNextVsync())和EventThread把SW-Vsyncevent callback到其感兴趣的client.需要注意的是App是通过SurfaceFlinger::createDisplayEventConnection()创建此连接的.而sufaceflinge是在其初始化时callEventQueue.setEventThread(mSFEventThread)创建的. 所以对App 的EventThread 来说可能有多个connection ,也有可能没有.而对sufaceflinger目前来说有且只有一个.
sp
创建 Connection连接.
status_tEventThread::registerDisplayEventConnection()
如其名所描述.其功能是把创建的Connection注册到一个容器中.当SW-VSYNCevent发生时,EventThread会从Connection注册的容器中,找到那些对SW-VSYNCevent感兴趣的connection并把vsyncevent通过BitTube传到client.
void EventThread::requestNextVsync()
Clinet 端通过Connectioncall 这函数通知EventThread,其对SW-SYNCevent的请求.
voidEventThread::onVSyncEvent(nsecs_t timestamp)
当SW-VSYNCEVENT 发生时,DispSyncSource 会call此函数,告知EventThread,Vsyncevent已经发生,如果此时有connect对Vsync感兴趣,EventThread便会通过connect->postEvent(event)把Vsync事件发送到client端(App或surfaceflinger).
bool EventThread::threadLoop()
线程的主体函数.其完成两件事.一是把对SW-VSYNCevent有请求并且还没有处理的connect找出来.而是把Vsyncevent通过connect通知到client.
Vector< sp
EventThread 的主要功能都在此函数里.此函数由threadLoop()调用.EventThread在大部分时间里是sleep的.如果系统的性能比较好,那么其sleep的节奏是和SW-VSYNCevent的节奏一致.即16.6mssleep一次.然而由于其App或surfaceflinger没有Vsync的请求,其sleep的时间为更长.此函数的名为waitForEvent,其到底在等什么event?原来此函数在等待的event就是Dispsync产生的SW-SYNCevent.其功能check所有的connect是否有Vsync事件请求根据不同的情况做如下处理.
所有的connect都没有Vsync请求,则其通过disableVSyncLocked(),disableVsync event,那么此EventThread将不会收到SW-SYNCevent,一直sleep直到有connect有Vsync请求为止.
在所有的connect中,有SW-SYNC event请求,但是当其请求SW-SYNCevent时,SW-SYNCevent还没有fire,则其通过enableVSyncLocked()enable Vsync并进入sleep.当下一个SW-SYNCevent来到时,便把所有有SW-SYNCevent请求的connection返回给threadLoop.
下图是big pic.
本图是一个非常典型的Androiddisplay系统如何利用Vsync-appevent来更新App的view.
App 通过Connection向EventThread请求SW-VSYNCevent(最终并且通过callEventThread::requestNextVsync()).一般来说App是通过Choreographer.doScheduleVsync()来请求Vsyncevent.
DispSync 产生SW-VSYNC event并call DispSyncSource的onDispSyncEvent().
DispSyncSource产生VSYNC-app信号跳变.
同时DispSyncSource使EventThread(app)的waitForEvent()结束sleep,并把VSYNC-app通知到App进程(gallery3d).
Gallery3d 进程收到VSYNC-app信号,在其main线程(UI Thread)中开始draw一个frame(发送draw command给GL thread).
GL Thread 开始draw view.
当GL thread 线程Draw 完一frame后,把draw buffer放到buffer queue中.
SufaceView中画好的buffer 数据增加为1.这个buffer 将被surfaceflinger 在适当的时候合成.
上图是一个典型的Vsync-sf的实例.
App draw 完一个frame (SurfaceView) 并且把此frame通过调用BufferQueueProducer::queueBuffer()放到 bufferqueue中,并且通过call EventThread::requestNextVsync(),请求SW-VSYNCevent.此时由于DispSync没有fireSW-Sync event所以EventThread在EventThread::waitForEvent()中sleep.
DispSync触发SW-SYNC event信号并且call DispSyncSource::onDispSyncEvent().
在DispSyncSource::onDispSyncEvent()中首先引起Vsync-sf跳变,然后在call EventThread::onVSyncEvent(nsecs_ttimestamp).
在EventThread::onVSyncEvent(nsecs_ttimestamp)中,会唤醒EventThread::waitForEvent()中的sleep,从而发送消息到Surfaceflinger.
SurfaceFlinger收到EventThread发过来的Vsync-sf跳变信息,开始合成dirty layer, 本例是SurfaceView.合成完以后发送更新的消息到DisplayHW.
Display HW 收到更新的消息时,HW vsync event还没有来,因此DisplayHW sleep了一段事件.当HWvsync event到来时,DisplayHW 便更新其LCD的内容.把最新的内容显示在LCD上.
自此我们完成了Vsync子系统的分析.回到系统性能上,我们可以看出系统的性能(FPS)可能于如下因素有关.
DispSync的调度. DispSync是Vsync系统的心脏,如果DispSync来不及调度,则有可能由于去SW-SYNC event的产生不及时而影响系统的性能.
View Draw 花费了过多的时间也会引性能问题, 如果其不能在一个SW-SYNC时间内完成, 那么此应用就会有一个Janks.由于现在的Androd系统都采用了HWUI,viewdraw往往是GPU直接draw,所以很多时候是GPUperformance问题.
HW display 的性能问题.LCD 上所绘画的内容, 最终需要HW display在HW vsync触发是把其内容显示在LCD上.
Binder 的调度问题, 我们可以看到无论是requestNextVsync () 还是queueBuffer()都是App通过binder与surfaceflinger进程通信的.而这些binder在大多数时间是sleep的.如果binder由于CPU调度而错过了一个Vsync跳变点,那么就有一个frame发生Janks.