原文网址(转载请注明出处):
http://blog.csdn.net/newchenxf/article/details/49131167

VSYNC是自android 4.1版本以后加入的,目的是为了改善android的流畅程度。
其实网上已经有不少朋友对android SurfaceFlinger的VSYNC做分析,比如下面的这些文章,本人也是阅读了很多别人家的文章后,深读代码,加log调试研究,才写的,感谢他们。
1. Android 4.4(KitKat)中的设计模式-Graphics子系统
http://blog.csdn.net/jinzhuojun/article/details/17427491
2. Android 4.4(KitKat)中VSync信号的虚拟化
http://blog.csdn.net/jinzhuojun/article/details/17293325
3. android4.1及后续版本的UI平滑技术
http://blog.csdn.net/sfrysh/article/details/8312766
4. Android应用程序UI硬件加速渲染环境初始化过程分析
http://blog.csdn.net/luoshengyang/article/details/45769759

现在Android已经到了6.0,不过我还没拉到6.0的代码,所以就先用5.1版本做分析。好了,现在我们开始。
我们将分3部分来讲解。一是什么是VSYNC,一是VSYNC如何从hardware传到surfaceflinger,一是VSYNC如何从surfaceflinger传到application。

一、什么是VSYNC

VSYNC是啥?他就是一个信号源。假设你的屏幕是60FPS的话,那意味着,每隔1s,那么屏幕就有60次中断信号产生,即,每隔16.666ms,就会有一次中断信号产生。这个能干嘛用呢?对于屏幕来说,它无非就是每次来一个中断信号,就刷新一下屏幕。
在Android 4.1以前,VSYNC和android没有半毛钱关系。但到了4.4及以后,android就引入了VSYNC,干嘛用呢?为的就是利用这个中断信号,来刷新UI,使得更新UI变得更加有序,流畅。

加入VSYNC以后,UI是如何画图和更新的呢?
首先,我们必须明白,android graphics的主干就是2件事:
一个是画图,一个是合成显示。
画图 需要android app调用Canvas API去画。至于最后是纯软件画,还是用OpenGL ES,也就是硬件加速去画,那就看具体情况了。这里不详说。
合成显示 需要surfaceflinger拿到app画好的几张图(因为当前可见的UI不只一个app,某个app也不一定只有一张图,所以说是,几张图,其实对于framework来说,图也可以认为是一个Surface,某一个最最普通的activity,它会有个layout对吧,那其实会对应一个Surface,当然了,如果你这layout里还有SurfaceView控件,那这个activity就有2个Surface了),合成为一张,然后送出去。如果你是机顶盒平台,那就是通过HDMI的驱动,输出到电视。如果你是某个android手机,那应该是送到linux的framebuffer driver,该驱动会把数据render到屏幕上。

那么,VSYNC作用就来了。

如图所示,Display代表合成显示,CPU/GPU代表画图。
在第1个VSYNC时间,CPU/GPU画图,Display还没拿到图,不显示。
第2个VSYNC时间,图1画好了,Display拿到,就显示它,同时,CPU/GPU也开始画第2个图。
第3个VSYNC时间,图2画好了,Display拿到,就显示它,同时,CPU/GPU也开始画第3个图。
如此往复。
当然了,上图是一个示意图,实际使用时,会用tripple buffer的机制,具体详见【android4.1及后续版本的UI平滑技术】。
另外,实际工作也会加上一定的偏移。

虽然有偏移,但大家工作既保持一定的节拍,又可以相互错开,一前一后保持着咚次哒次,也挺好的,还可以让CPU能错开工作高峰。其中两个Phase offset参数(即VSYNC_EVENT_PHASE_OFFSET_NS和SF_VSYNC_EVENT_PHASE_OFFSET_NS)是可调的。
具体详见【Android 4.4(KitKat)中VSync信号的虚拟化

总而言之,有2个地方需要用VSYNC,一个是surfaceflinger,每来一个VSYNC,就合成显示一次。一个是application,每来一个VSYNC,就画一张图。

二、VSYNC如何从hardware传到surfaceflinger

理论上,VSYNC是硬件产生的,是一个中断信号。比如,我当前做的机顶盒项目,这中断信号是HDMI产生的,我的电视是60FPS,因此每16.666..ms就有一个中断。一般ODM厂商已有驱动程序,会接收该中断,其他进程可以通过ioctl来间接接收该中断。假设该驱动是disp_driver。
我们知道,Android Graphics有一个 HWComposer HAL库,这是ODM实现的。其实,这个库也要完成VSYNC的工作。它通过与disp_driver通信,去监听硬件产生的VSYNC中断,收到以后,立马通过callback函数,返回给android framework 的HWComposer。当然啦,framework的HWComposer也得向它的HAL注册callback,不然HAL就没法回调了。
注册函数是:
hwc_composer_device_1 -> registerProcs
VSYNC的回调函数是:
HWComposer -> hook_vsync

对android来说,Graphics的核心进程时surfaceflinger,它主要用于合成各种surface并显示。HWComposer HAL是它加载的。显然HAL的VSYNC callback也是给他的。

surfaceflinger有4个线程与VSYNC有关。
1个EventControlThread,用于开始/关闭 HWComposer HAL的VSYNC。
1个是DispSyncThread,用于接收HWComposer HAL的VSYNC信号,并分发给EventThread。
2个EventThread。之所以是2个,是因为surfaceflinger创建了2个EventThread对象,一个给application用,一个给自己用。如上文所说,application用它来画图,surfaceflinger用它来显示。EventThread类主要是接收DispSyncThread发来的VSYNC,并分发给真正使用的人。
这些线程是在surfaceflinger初始化的时候创建的。
代码如下:

void SurfaceFlinger::init() {......    // start the EventThread    //mPrimaryDispSync is DispSync    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,            vsyncPhaseOffsetNs, true, "app");    mEventThread = new EventThread(vsyncSrc);    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,            sfVsyncPhaseOffsetNs, true, "sf");    mSFEventThread = new EventThread(sfVsyncSrc);    mEventQueue.setEventThread(mSFEventThread);    mEventControlThread = new EventControlThread(this);    mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);}DispSync::DispSync() :        mRefreshSkipCount(0),        mThread(new DispSyncThread()) {    //start DispSyncThread    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);

从代码可知,2个EventThread对象分别是mEventThread,给app用,mSFEventThread,给surfaceflinger自己用。

下面给出这4个Thread关系图。

从这个图,我们可以清晰的看出VSYNC的传递过程。

HWComposer HAL通过callback函数,把VSYNC信号传给DispSyncThread,DispSyncThread传给EventThread。这就是本节要讨论的,VSYNC如何从hardware传到android framework。
postEvent是干嘛呢?它用于把VSYNC传递给app,是下节要讨论的内容。这里先简单描述一下:
如果是app用的话,那么,app会向EventThread注册一个connection(每个app启动时,都会注册)。所以该EventThread会向所有的connection发送VSYNC信号(通过postEvent 函数)。
而如果是surfaceflinger自己用的话,就简单了,就自己一个connection。
上面代码的mEventQueue.setEventThread(mSFEventThread)就是surfaceflinger自己注册connection的。
且看该函数的实现

void MessageQueue::setEventThread(const sp<EventThread>& eventThread){    mEventThread = eventThread;    mEvents = eventThread->createEventConnection();    mEventTube = mEvents->getDataChannel();    mLooper->addFd(mEventTube->getFd(), 0, Looper::EVENT_INPUT,            MessageQueue::cb_eventReceiver, this);}

通过createEventConnection向EventThread注册了connection。
surfaceflinger如何根据vsync的节奏来工作,比较简单,和app如何根据vsync的节奏来工作差不多,所以本文只讨论app如何使用vsync,也就是第三节的内容。

前面的模块图毕竟比较简约,我这里给出详细的,VSYNC如何从HWComposer HAL发到EventThread。

从图中可知,HWComposer HAL层获得硬件中断,然后通过callback函数,回调到SurfaceFlinger的onVsyncReceived。
SurfaceFlinger调用DispSync的updateModel函数,唤醒DispSyncThread。
DispSyncThread通过fireCallbackInvocations函数,告诉DispSyncSource有VSYNC了。

    void fireCallbackInvocations(const Vector& callbacks) {        for (size_t i = 0; i < callbacks.size(); i++) {            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);        }    }

mCallback就是DispSyncSource,因为他继承了DispSync::Callback。
所以,就调用到了DispSyncSource的onDispSyncEvent函数。

    virtual void onDispSyncEvent(nsecs_t when) {        sp<VSyncSource::Callback> callback;        {            Mutex::Autolock lock(mMutex);            callback = mCallback;            ......        }        if (callback != NULL) {            callback->onVSyncEvent(when);        }    }

对DispSyncSource来说,它的mCallback成员变量,是EventThread(EventThread继承了VSyncSource::Callback)。
所以,又调用了EventThread的onVSyncEvent函数。
这么说来,其实我们可以把DispSyncSource理解为DispSyncThread与EventThread的桥梁。

接着,来看onVSyncEvent。

void EventThread::onVSyncEvent(nsecs_t timestamp) {    Mutex::Autolock _l(mLock);    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;    mVSyncEvent[0].header.id = 0;    mVSyncEvent[0].header.timestamp = timestamp;    mVSyncEvent[0].vsync.count++;    mCondition.broadcast();}

他也比较简单,存下vsync,然后唤醒EventThread。
EventThread的threadLoop唤醒后,通过postEvent,向所有注册过的connection发送VSYNC消息。

VSYNC从surfaceflinger到app

上一节讲了VSYNC从HAL到framework(其实就是surfaceflinger),这一节就讲讲VSYNC从surfaceflinger到app。
其实就是那个给app用的EventThread如何把VSYNC送到app。

首先,每个app创建时,都会new一个RenderThread(有且只有一个)。看名字就知道,它用于管理app UI 。
它初始化的时候,会new DisplayEventReceiver

void RenderThread::initializeDisplayEventReceiver() {    ......    mDisplayEventReceiver = new DisplayEventReceiver();    ......    // Register the FD    mLooper->addFd(mDisplayEventReceiver->getFd(), 0,            Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);}

DisplayEventReceiver是啥?
看名字就知道,它用于接收VSYNC。
首先,对于EventThread,其connection是这么定义的。
它是一个binder调用。
API定义在IDisplayEventConnection,总共就3个函数。

class IDisplayEventConnection : public IInterface{public:    /*     * getDataChannel() returns a BitTube where to receive the events from     */    virtual sp getDataChannel() const = 0;    virtual void setVsyncRate(uint32_t count) = 0;    virtual void requestNextVsync() = 0;};

服务端的具体实现是Connection类,它继承了BnDisplayEventConnection ,并具体实现了3个函数。
定义在frameworks/native/services/surfaceflinger/EventThread.h

class Connection : public BnDisplayEventConnection {public:    Connection(const sp& eventThread);    status_t postEvent(const DisplayEventReceiver::Event& event);        ....private:    virtual ~Connection();    virtual void onFirstRef();    virtual sp getDataChannel() const;    virtual void setVsyncRate(uint32_t count);    virtual void requestNextVsync();    // asynchronous    sp const mEventThread;    sp const mChannel;};

这个类最重要的成员变量,就是mChannel,它是BitTube类。BitTube是啥?
字面意思就是字节管道。如果看它的构造函数,就能发现,其实它就是包了socket,用于进程间通信。

void BitTube::init(size_t rcvbuf, size_t sndbuf) {    int sockets[2];    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {        size_t size = DEFAULT_SOCKET_BUFFER_SIZE;        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));        // sine we don't use the "return channel", we keep it small...        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));        fcntl(sockets[0], F_SETFL, O_NONBLOCK);        fcntl(sockets[1], F_SETFL, O_NONBLOCK);        mReceiveFd = sockets[0];        mSendFd = sockets[1];    } else {        mReceiveFd = -errno;        ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));    }}

DisplayEventReceiver构造函数,就首先会调用surfaceflinger去注册一个connection,connection又会创建BitTube。客户端便可以通过getDataChannel()获得这个BitTube。
那么进程通信就可以开始了。
surfaceflinger进程的EventThread把VSYNC不断的往BitTube写(想象成往socket写),而app进程将会创建DisplayEventReceiver,该app进程又可以不断的读BitTube(想象成读socket)。这样,VSYNC就可以传输了。

那么,谁会创建DisplayEventReceiver呢?是传说中的RenderThread。
如上文所说,每个app创建,都会new一个RenderThread,用于管理UI的画图。如同上面讲的4个线程, 这个RenderThread也是继承Thread类,当我们调用它的成员函数run的时候,就会创建一个新的线程。这个新的线程的入口点函数为RenderThread类的成员函数threadLoop。

RenderThread类有一个成员变量mQueue,描述的是一个Task Queue。这个Queue用于保存所有需要执行的Task,每一个Task都是用一个RenderTask对象来描述。同时,RenderTask类有一个成员变量mRunAt,用来表明Task的执行时间。这样,保存在Task Queue的Task就可以按照执行时间从先到后的顺序排序,这个mRunAt最终会存VSYNC。于是,RenderThread类的成员函数nextTask通过判断排在队列头的Task的执行时间是否小于等于当前时间,就可以知道当前是否有Task需要执行。如果有Task需要执行的话,就将它返回给调用者。我们可以把Task当做是画图任务。

这里给出RenderThread创建DisplayEventReceiver的代码。

void RenderThread::initializeDisplayEventReceiver() {    LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");    mDisplayEventReceiver = new DisplayEventReceiver();    status_t status = mDisplayEventReceiver->initCheck();    LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver "            "failed with status: %d", status);    // Register the FD    mLooper->addFd(mDisplayEventReceiver->getFd(), 0,            Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);}

这个函数的一个语句很重要:
mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
它表示,监听一个文件描述符,如果有变化,就执行回调函数displayEventReceiverCallback。
那么,mDisplayEventReceiver->getFd()获得的文件描述符是啥?
且看定义

int DisplayEventReceiver::getFd() const {    //mDataChanel is BitTube    if (mDataChannel == NULL)                return NO_INIT;    return mDataChannel->getFd();}

再看BitTube的定义

int BitTube::getFd() const{    return mReceiveFd;}

啊,原来,这个mReveivedRd就是sockets[0]。

而回调函数displayEventReceiverCallback干嘛呢?查看函数的调用顺序,就可以看到,其实它就是去读sockets[0]。
所以一切就明白了,surfaceflinger进程与某个app进程传递VSYNC,就是surfaceflinger的EventThread线程与app的RenderThread通过socket通信来传递。
下面给出流程图。

那么,RenderThread通过displayEventReceiverCallback回调,获得VSYNC后,会干嘛呢?当然就是画图啦!!
文章【Android应用程序UI硬件加速渲染环境初始化过程分析】有比较详细的介绍,我这里忽略一些条件判断等各种细节,大概描述一下主体流程:
displayEventReceiverCallback会调用drainDisplayEventQueue,请看这个函数。

void RenderThread::drainDisplayEventQueue() {    ATRACE_CALL();    nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);    if (vsyncEvent > 0) {        mVsyncRequested = false;        if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) {            ATRACE_NAME("queue mFrameCallbackTask");            mFrameCallbackTaskPending = true;            nsecs_t runAt = (vsyncEvent + DISPATCH_FRAME_CALLBACKS_DELAY);            queueAt(mFrameCallbackTask, runAt);        }    }}

首先通过latestVsyncEvent获得最新发出的VSYNC时间,然后,通过queueAt,把mFrameCallbackTask及对应的VSYNC时间插入Task Queue,并调用“mLooper->wake();”唤醒线程。
来看线程函数:

bool RenderThread::threadLoop() {    ......    initThreadLocals();    int timeoutMillis = -1;    for (;;) {        int result = mLooper->pollOnce(timeoutMillis);        LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,                "RenderThread Looper POLL_ERROR!");        nsecs_t nextWakeup;        // Process our queue, if we have anything        while (RenderTask* task = nextTask(&nextWakeup)) {            task->run();            // task may have deleted itself, do not reference it again        }        ......    }}

线程唤醒后,执行task->run()。他会干嘛呢?
查看其定义知,就是调用
mRenderThread->dispatchFrameCallbacks()。
而这个函数就是真正去调用画图啦!
代码片段如下,doFrame()是真正干活的函数。

void RenderThread::dispatchFrameCallbacks() {    ......    for (std::set::iterator it = callbacks.begin(); it != callbacks.end(); it++) {        (*it)->doFrame();    }}

总结一下就是。
1. App进程创建了RenderThread对象,RenderThread又创建了DisplayEventReceiver对象。
2. DisplayEventReceiver获得surfaceflinger的client handle,然后通过函数createDisplayEventConnection,向EventThread注册一个IDisplayEventConnection。这其实是binder调用。
3. DisplayEventReceiver通过IDisplayEventConnection->getDataChannel()获得BitTube对象,该对象包了socket管道的文件句柄,用于后面两个进程传递VSYNC。
4. 这些都准备好以后,一切都开始咚次哒次的跑起来。SurfaceFlinger的EventThread线程通过socket与App的RenderThread传递VSYNC时间。RenderThread获得VSYNC后,根据VSYNC的节拍,通过调用doFrame去最终画图。

结语

android graphics非常复杂庞大,VSYNC只是冰山一角。未来还有更多的知识值得学习。。。

更多相关文章

  1. C语言函数的递归(上)
  2. Android(安卓)Framework层看硬件加速
  3. Android网络状态实时监听实例代码(二)
  4. Android网络编程 HttpUrlConnection HttpClient AsyncTask
  5. linux kernel suspend Resume
  6. 关于Sytem.gc()主动触发Android(安卓)GC
  7. Android(安卓)客户端与服务器端进行数据交互(二、登录客户端)
  8. Android(安卓)Says Bonjour
  9. Handler、Looper与MessageQueue源码分析

随机推荐

  1. android 界面 滑入 效果
  2. android-数据库操作实例,留着以后用
  3. android 的popwindow弹窗
  4. android计算器简单版【安卓进化一】
  5. Android:ImageView 设置图片
  6. Android(安卓)添加桌面快捷方式操作
  7. android 获得监听某一广播的所有程序
  8. android 应用异常可以引起android系统崩
  9. Android: How to play Tone sound
  10. Android(安卓)文件下载工具类