http://blog.csdn.net/lee_3do/article/details/39288211
VSYNC 的概念
VSYNC(Vertical Synchronization)是一个相当古老的概念,对于游戏玩家,它有一个更加大名鼎鼎的中文名字—-垂直同步。
“垂直同步(vsync)”指的是显卡的输出帧数和屏幕的垂直刷新率相同,这完全是一个CRT显示器上的概念。其实无论是VSYNC还是垂直同步这个名字,因为LCD根本就没有垂直扫描的这种东西,因此这个名字本身已经没有意义。但是基于历史的原因,这个名称在图形图像领域被沿袭下来。
在当下,垂直同步的含义我们可以理解为,使得显卡生成帧的速度和屏幕刷新的速度的保持一致。举例来说,如果屏幕的刷新率为60Hz,那么生成帧的速度就应该被固定在1/60 s。
Android中的VSYNC — 黄油计划
从Android 4.1开始,谷歌致力于解决Android系统中最饱受诟病的一个问题,滑动不如iOS流畅。因谷歌在4.1版本引入了一个重大的改进—Project Butter,也即是黄油计划。
Project Butter对Android Display系统进行了重构,引入了三个核心元素,即VSYNC、Triple Buffer和Choreographer。关于后面两个概念我们会在后面开专题讲解,这里我们重点讲解VSYNC的作用。(关于黄油计划,网上已经有大量文字讲解说明,请参考http://blog.csdn.net/innost/article/details/8272867http://blog.csdn.net/xuesen_lin/article/details/8954869)。
玩过大型PC游戏的玩家都知道,VSYNC最重要的作用是防止出现画面撕裂(screentearing)。所谓画面撕裂,就是指一个画面上出现了两帧画面的内容,如下图。
为什么会出现这种情况呢?这种情况一般是因为显卡输出帧的速度高于显示器的刷新速度,导致显示器并不能及时处理输出的帧,而最终出现了多个帧的画面都留在了显示器上的问题。这也就是我们所说的画面撕裂。
提到垂直同步这里就多提一句,其实我认为对于PC上的大型游戏来说,只有配置足够高,高到显卡输出帧率可以稳定的高于显示器的刷新频率,才有开启垂直同步的必要。因为只有这个时候,画面撕裂才会真正成为一个问题。而对于很多情况下主机性能不足导致游戏输出帧率低于显示器的刷新频率的情况下,尤其是帧率稳定在40~60之间时,开启垂直同步可能会导致帧率倍数级的下降(具体原因我们在Graphic架构一文中提到过,当帧生成速度不及VSync速度时,帧率的下降不是平缓的,而且很可能是倍数级的。当然这在android系统上并非严重问题,因为android上很少有高速的复杂场景的频繁切换。事实上,在Android的普通应用场景下,VSync的使用不仅不会降低帧率,还可以有效解决卡顿问题)。
回到正文中来,那么VSync除了可以解决画面的撕裂的问题,还可以解决别的什么问题吗?我们来看下图:
这个图中有三个元素,Display是显示屏幕,GPU和CPU负责渲染帧数据,每个帧以方框表示,并以数字进行编号,如0、1、2等等。VSync用于指导双缓冲区的交换。
以时间的顺序来看下将会发生的异常:
Step1. Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,而且赶在Display显示下一帧前完成
Step2. 因为渲染及时,Display在第0帧显示完成后,也就是第1个VSync后,正常显示第1帧
Step3. 由于某些原因,比如CPU资源被占用,系统没有及时地开始处理第2帧,直到第2个VSync快来前才开始处理
Step4. 第2个VSync来时,由于第2帧数据还没有准备就绪,显示的还是第1帧。这种情况被Android开发组命名为“Jank”。
Step5. 当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync。
所以总的来说,就是屏幕平白无故地多显示了一次第1帧。原因大家应该都看到了,就是CPU没有及时地开始着手处理第2帧的渲染工作,以致“延误军机”。
其实总结上面的这个情况之所以发生,首先的原因就在于第二帧没有及时的绘制(当然即使第二帧及时绘制,也依然可能出现Jank,这就是同时引入三重缓冲的作用。我们将在三重缓冲一节中再讲解这种情况)。那么如何使得第二帧即使被绘制呢?
这就是我们在Graphic系统中引入VSYNC的原因,考虑下面这张图:
如上图所示,一旦VSync出现后,立刻就开始执行下一帧的绘制工作。这样就可以大大降低Jank出现的概率。另外,VSYNC引入后,要求绘制也只能在收到VSYNC消息之后才能进行,因此,也就杜绝了另外一种极端情况的出现—-CPU(GPU)一直不停的进行绘制,帧的生成速度高于屏幕的刷新速度,导致生成的帧不能被显示,只能丢弃,这样就出现了丢帧的情况—-引入VSYNC后,绘制的速度就和屏幕刷新的速度保持一致了。
VSYNC信号的生成
那么VSYNC信号是如何生成的呢?
Android系统中VSYNC信号分为两种,一种是硬件生成的信号,一种是软件模拟的信号。
硬件信号是由HardwareComposer提供的,HWC封装了相关的HAL层,如果硬件厂商提供的HAL层实现能定时产生VSYNC中断,则直接使用硬件的VSYNC中断,否则HardwareComposer内部会通过VSyncThread来模拟产生VSYNC中断(其实现很简单,就是sleep固定时间,然后唤醒)。
回到我们上一节中讲到的SurfaceFlinger的启动过程inti函数上来,上一节我们提到init函数内会创建一个HWComposer对象。
- HWComposer::HWComposer(
- const sp<SurfaceFlinger>& flinger,
- EventHandler& handler)
- : mFlinger(flinger),
- mFbDev(0), mHwc(0), mNumDisplays(1),
- mCBContext(new cb_context),
- mEventHandler(handler),
- mDebugForceFakeVSync(false)
- {
- ...
- //首先是一些和VSYNC有关的信息的初始化
- //因为在硬件支持的情况下,VSYNC的功能就是由HWC提供的
- for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
- mLastHwVSync[i] = 0;
- mVSyncCounts[i] = 0;
- }
- //根据配置来看是否需要模拟VSYNC消息
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.no_hw_vsync", value, "0");
- mDebugForceFakeVSync = atoi(value);
- ...
- // don't need a vsync thread if we have a hardware composer
- needVSyncThread = false;
- // always turn vsync off when we start,只是暂时关闭信号,后面会再开启
- eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
- //显然,如果需要模拟VSync信号的话,我们需要线程来做这个工作
- if (needVSyncThread) {
- // we don't have VSYNC support, we need to fake it
- //VSyncThread类的实现很简单,无非就是一个计时器而已,定时发送消息而已
- //TODO VSYNC专题
- mVSyncThread = new VSyncThread(*this);
- }
- ...
- }
- HWComposer::HWComposer(
- const sp<SurfaceFlinger>& flinger,
- EventHandler& handler)
- : mFlinger(flinger),
- mFbDev(0), mHwc(0), mNumDisplays(1),
- mCBContext(new cb_context),
- mEventHandler(handler),
- mDebugForceFakeVSync(false)
- {
- ...
- //首先是一些和VSYNC有关的信息的初始化
- //因为在硬件支持的情况下,VSYNC的功能就是由HWC提供的
- for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
- mLastHwVSync[i] = 0;
- mVSyncCounts[i] = 0;
- }
- //根据配置来看是否需要模拟VSYNC消息
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.no_hw_vsync", value, "0");
- mDebugForceFakeVSync = atoi(value);
- ...
- // don't need a vsync thread if we have a hardware composer
- needVSyncThread = false;
- // always turn vsync off when we start,只是暂时关闭信号,后面会再开启
- eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
- //显然,如果需要模拟VSync信号的话,我们需要线程来做这个工作
- if (needVSyncThread) {
- // we don't have VSYNC support, we need to fake it
- //VSyncThread类的实现很简单,无非就是一个计时器而已,定时发送消息而已
- //TODO VSYNC专题
- mVSyncThread = new VSyncThread(*this);
- }
- ...
- }
我们来看下上面这段代码。
首先mDebugForceFakeVSync是为了调制,可以通过这个变量设置强制使用软件VSYNC模拟。
然后针对不同的屏幕,初始化了他们的mLastHwVSync和mVSyncCounts值。
如果硬件支持,那么就把needVSyncThread设置为false,表示不需要软件模拟。
接着通过eventControl来暂时的关闭了VSYNC信号,这一点将在下面讲解eventControl时一并讲解。
最后,如果需要软件模拟Vsync信号的话,那么我们将通过一个单独的VSyncThread线程来做这个工作(fake VSYNC是这个线程唯一的作用)。我们来看下这个线程。
软件模拟
- bool HWComposer::VSyncThread::threadLoop() {
- const nsecs_t period = mRefreshPeriod;
- //当前的时间
- const nsecs_t now = systemTime(CLOCK_MONOTONIC);
- //下一次VSYNC到来的时间
- nsecs_t next_vsync = mNextFakeVSync;
- //为了等待下个时间到来应该休眠的时间
- nsecs_t sleep = next_vsync - now;
- //错过了VSYNC的时间
- if (sleep < 0) {
- // we missed, find where the next vsync should be
- //重新计算下应该休息的时间
- sleep = (period - ((now - next_vsync) % period));
- //更新下次VSYNC的时间
- next_vsync = now + sleep;
- }
- //更新下下次VSYNC的时间
- mNextFakeVSync = next_vsync + period;
- struct timespec spec;
- spec.tv_sec = next_vsync / 1000000000;
- spec.tv_nsec = next_vsync % 1000000000;
- int err;
- do {
- //纳秒精度级的休眠
- err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
- } while (err<0 && errno == EINTR);
- if (err == 0) {
- //休眠之后,到了该发生VSYNC的时间了
- mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
- }
- return true;
- }
这个函数其实很简单,无非就是一个简单的时间计算,计算过程我已经写在了程序注释里面。总之到了应该发生VSYNC信号的时候,就调用了mHwc.mEventHandler.onVSyncReceived(0, next_vsync)函数来通知VSYNC的到来。
我们注意到mEventHandler实际上是在HWC创建时被传入的,我们来看下HWC创建时的代码.
- mHwc = new HWComposer(this,
- *static_cast<HWComposer::EventHandler *>(this));
- class SurfaceFlinger : public BnSurfaceComposer,
- private IBinder::DeathRecipient,
- private HWComposer::EventHandler
可以看到这个mEventHandler实际上就是SurfaceFlinger。也就是说,VSYNC信号到来时,SurfaceFlinger的onVSyncReceived函数处理了这个消息。
这里我们暂时先不展开SurfaceFlinger内的逻辑处理,等我们下面分析完硬件实现后,一并进行分析
硬件实现
上面我们讲了软件如何模拟一个VSYNC信号并通知SurfaceFlinger,那么硬件又是如何实现这一点的呢?
我们再一次回到HWC的创建过程中来:
- if (mHwc) {
- ALOGE("Lee Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
- (hwcApiVersion(mHwc) >> 24) & 0xff,
- (hwcApiVersion(mHwc) >> 16) & 0xff);
- if (mHwc->registerProcs) {
- mCBContext->hwc = this;
- mCBContext->procs.invalidate = &hook_invalidate;
- mCBContext->procs.vsync = &hook_vsync;
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
- mCBContext->procs.hotplug = &hook_hotplug;
- else
- mCBContext->procs.hotplug = NULL;
- memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
- mHwc->registerProcs(mHwc, &mCBContext->procs);
- }
来看下上面这段实现。
当HWC有vsync信号生成时,硬件模块会通过procs.vsync来通知软件部分,因此也就是调用了hook_vsync函数。
- void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
- int64_t timestamp) {
- cb_context* ctx = reinterpret_cast<cb_context*>(
- const_cast<hwc_procs_t*>(procs));
- ctx->hwc->vsync(disp, timestamp);
- }
- void HWComposer::vsync(int disp, int64_t timestamp) {
- //只有真实的硬件设备才会产生VSYNC
- if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
- {
- mLastHwVSync[disp] = timestamp;
- }
- mEventHandler.onVSyncReceived(disp, timestamp);
- }
我们发现最后殊途同归,硬件信号最终也通过onVSyncReceived函数通知到了SurfaceFlinger了。下面我们来分析下SurfaceFlinger的处理过程。
Surfaceflinger对VSYNC消息的处理
在4.4之前,本来SurfaceFlinger对VSYNC的处理比较简单,只是通知EventThread进行处理即可,但是KK再次对VSYNC这一段的逻辑进一步的细化和复杂化。不得不说,Google为了提升Android UI的流畅性真是费尽心思。
虽然只是一个简单的信号,但是KK的处理已经相当复杂。先来直接看下Surfaceflinger的onVSyncReceived函数:
- void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
- bool needsHwVsync = false;
- {
- if (type == 0 && mPrimaryHWVsyncEnabled) {
- needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
- }
- }
- if (needsHwVsync) {
- enableHardwareVsync();
- } else {
- disableHardwareVsync(false);
- }
- }
这段代码很短,但是相当让人困惑,困惑至少有以下三点:
- mPrimaryHWVsyncEnabled是干嘛的,是被什么时候赋值的?
- enableHardwareVsync和disableHardwareVsync又起到了什么作用?
- mPrimaryDispSync是什么?addResyncSample有什么作用?
Surfaceflinger的init函数
要回答这三个问题,我们首先还是得回到SurfaceFlinger的init函数中来。
EventThread的创建
上一节中SF的创建过程中我们提到了EventThread的创建。
- {
- ...
- // start the EventThread
- sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- vsyncPhaseOffsetNs, true);
- mEventThread = new EventThread(vsyncSrc);
- sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- sfVsyncPhaseOffsetNs, true);
- mSFEventThread = new EventThread(sfVsyncSrc);
- mEventQueue.setEventThread(mSFEventThread);
- mEventControlThread = new EventControlThread(this);
- mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
- // set a fake vsync period if there is no HWComposer
- if (mHwc->initCheck() != NO_ERROR) {
- mPrimaryDispSync.setPeriod(16666667);
- }
- ...
- }
这里看起来更奇怪了。首先这里有两个看起来几乎一样的DispSyncSource,看起来唯一的区别就是一个偏移时间的不同。
而其实,这两个DispSyncSource就是KK引入的重大变化。Android 4.4(KitKat)引入了VSync的虚拟化,即把硬件的VSync信号先同步到一个本地VSync模型中,再从中一分为二,引出两条VSync时间与之有固定偏移的线程。示意图如下:
而Google这样修改的目的又是什么呢?
回忆一下我们在Graphic架构一文中指出的,在当前三重缓冲区的架构下,即对于一帧内容,先等App UI画完了,SurfaceFlinger再出场对其进行合并渲染后放入framebuffer,最后整到屏幕上。而现有的VSync模型是让大家一起开始干活。
这个架构其实会产生一个问题,因为App和SurfaceFlinger被同时唤醒,导致他们二者总是一起工作,必然导致VSync来临的时刻,这二者之间产生了CPU资源的抢占。因此,谷歌给这两个工作都加上一个小小的延迟,让这两个工作并不是同时被唤醒,这样大家就可以错开使用资源的高峰期,提高工作的效率。
这两个延迟,其实就分别对应上面代码中的vsyncSrc(绘制延迟)和sfVsyncSrc(合成延迟)。
了解了这些背景知识后,我们来继续看代码,DispSyncSource的初始化如此简单,这里不再详述(当然这里的变量mPrimaryDispSync的值来源让人困惑)。
在创建了两个DispSyncSource变量后,我们使用它们来初始化了两个EventThread。下面我们来详细看下EventThread的创建流程:
- EventThread::EventThread(const sp<VSyncSource>& src)
- : mVSyncSource(src),
- mUseSoftwareVSync(false),
- mVsyncEnabled(false),
- mDebugVsyncEnabled(false) {
- for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
- mVSyncEvent[i].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[i].header.id = 0;
- mVSyncEvent[i].header.timestamp = 0;
- mVSyncEvent[i].vsync.count = 0;
- }
- }
- void EventThread::onFirstRef() {
- run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
- }
EventThread的构造函数很简单。重点是它的onFirstRef函数启动了一个EventThread线程,于是下面的代码才是重点:
- bool EventThread::threadLoop() {
- DisplayEventReceiver::Event event;
- Vector< sp<EventThread::Connection> > signalConnections;
- signalConnections = waitForEvent(&event);
- // dispatch events to listeners...
- const size_t count = signalConnections.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<Connection>& conn(signalConnections[i]);
- // now see if we still need to report this event
- status_t err = conn->postEvent(event);
- if (err == -EAGAIN || err == -EWOULDBLOCK) {
- // The destination doesn't accept events anymore, it's probably
- // full. For now, we just drop the events on the floor.
- // FIXME: Note that some events cannot be dropped and would have
- // to be re-sent later.
- // Right-now we don't have the ability to do this.
- ALOGW("EventThread: dropping event (%08x) for connection %p",
- event.header.type, conn.get());
- } else if (err < 0) {
- // handle any other error on the pipe as fatal. the only
- // reasonable thing to do is to clean-up this connection.
- // The most common error we'll get here is -EPIPE.
- removeDisplayEventConnection(signalConnections[i]);
- }
- }
- return true;
- }
上面的函数本身并不复杂,其中调用了一个waitForEvent的函数。这个函数相当之长,为了防止代码展开太多,我们这里暂时不再详细分析这个函数。我们目前只需要知道这个函数的最重要的作用是等待Event的到来,并且查找对event感兴趣的监听者,而在没有event到来时,线程处于休眠状态,等待event的唤醒(我们将在下一篇文章VSYNC的接收和处理中展开分析这个函数)。
这样,EventThread线程就运行起来,处在等待被event唤醒的状态下。
MessageQueue和EventThread建立连接
简单说明完EventThread之后,我们再次回到SurfaceFlinger的init过程中来。回到刚才我们本小节开始我们分析的代码中来:
- {
- ...
- // start the EventThread
- sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- vsyncPhaseOffsetNs, true);
- mEventThread = new EventThread(vsyncSrc);
- sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- sfVsyncPhaseOffsetNs, true);
- mSFEventThread = new EventThread(sfVsyncSrc);
- mEventQueue.setEventThread(mSFEventThread);
- ...
- }
下面这句 mEventQueue.setEventThread(mSFEventThread);其实很重要,这个函数将SurfaceFlinger的MessageQueue真正和我们刚才创建的EventThread建立起了连接,这样SurfaceFlinger才能真正接收到来自HWC的VSYNC信号。
我们来看下这段代码:
- void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
- {
- mEventThread = eventThread;
- mEvents = eventThread->createEventConnection();
- mEventTube = mEvents->getDataChannel();
- mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT,
- MessageQueue::cb_eventReceiver, this);
- }
这里代码逻辑其实很简单,就是创建了一个到EventThread的连接,得到了发送VSYNC事件通知的BitTube,然后监控这个BitTube中的套接字,并且指定了收到通知后的回调函数,MessageQueue::cb_eventReceiver。这样一旦VSync信号传来,函数cb_eventReceiver将被调用。
上面这个过程的原理,应该是使用了linux中的socket通信原理,因为和本文主题关系不大,不再展开。而MessageQueue在收到VSync消息后做了什么,我们将在下一篇文章VSync的接收和处理中详细说明。
EventControlThread
回到SurfaceFlinger的init过程中来,在EventThread创建好,MessageQueue和EventThread建立连接之后,SurfaceFlinger又创建了一个EventControlThread。
- {
- ...
- mEventQueue.setEventThread(mSFEventThread);
- mEventControlThread = new EventControlThread(this);
- mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
- // set a fake vsync period if there is no HWComposer
- if (mHwc->initCheck() != NO_ERROR) {
- mPrimaryDispSync.setPeriod(16666667);
- }
- ...
- }
我们来看下这个EventControlThread是做什么的:
- EventControlThread::EventControlThread(const sp<SurfaceFlinger>& flinger):
- mFlinger(flinger),
- mVsyncEnabled(false) {
- }
- bool EventControlThread::threadLoop() {
- Mutex::Autolock lock(mMutex);
- bool vsyncEnabled = mVsyncEnabled;
- mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC,
- mVsyncEnabled);
- while (true) {
- status_t err = mCond.wait(mMutex);
- if (err != NO_ERROR) {
- ALOGE("error waiting for new events: %s (%d)",
- strerror(-err), err);
- return false;
- }
- if (vsyncEnabled != mVsyncEnabled) {
- mFlinger->eventControl(HWC_DISPLAY_PRIMARY,
- SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled);
- vsyncEnabled = mVsyncEnabled;
- }
- }
- return false;
- }
- void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
- getHwComposer().eventControl(disp, event, enabled);
- }
其实EventControlThread的功能很简单,从名字里面我们就可以看出来,这个线程最重要的作用就是给HWC硬件发送消息,来通知开启或者关闭VSync消息的。
从代码来看,线程开始时,根据当前状态来通知硬件是否要发送VSync信号(刚开始时应该是关闭的)。然后进入等待:mCond.wait(mMutex),等待的信号来源也很简单:
- void EventControlThread::setVsyncEnabled(bool enabled) {
- Mutex::Autolock lock(mMutex);
- mVsyncEnabled = enabled;
- mCond.signal();
- }
也就是说,一旦有人调用了EventControlThread的setVsyncEnabled函数时,将发送通知,然后条件被触发,EventControlThread根据当前最新的状态来重新给硬件发送命令,来告知硬件是否要开启VSync信号。那么是谁在什么时候调用了setVsyncEnabled函数呢?我们这里先不跟进,等到下一小节我们讲完屏幕的点亮和熄灭后大家就会明白这一段。
onScreenAcquired
Surfaceflinger的init函数中,除了和EventThread相关的代码外,还有一段显示设备的初始化:
- {
- ...
- // set initial conditions (e.g. unblank default device)
- initializeDisplays();
- ...
- }
我们在上一篇文档SF的创建过程中已经讲解过,其中initializeDisplays函数调用了一个重要的函数onScreenAcquired,前面文档中我们也说过,这个函数不仅在这里会被调用,同样会在屏幕点亮时被调用。
- void SurfaceFlinger::onScreenAcquired(const sp<const DisplayDevice>& hw) {
- ...
- if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- // built-in display, tell the HWC
- getHwComposer().acquire(type);
- if (type == DisplayDevice::DISPLAY_PRIMARY) {
- // FIXME: eventthread only knows about the main display right now
- mEventThread->onScreenAcquired();
- resyncToHardwareVsync(true);
- }
- }
- ...
- }
首先我们来看下EventThread类的onScreenAcquired函数:
- void EventThread::onScreenAcquired() {
- Mutex::Autolock _l(mLock);
- if (mUseSoftwareVSync) {
- // resume use of h/w vsync
- mUseSoftwareVSync = false;
- mCondition.broadcast();
- }
- }
记得我们前面在讲EventThread时提到的,EventThread开始threadLoop以后,会在waitForEvent中等待被通知,而这个被等待的通知。就是在这个onScreenAcquired函数中发出的通知,mCondition.broadcast()(当VSYNC信号到达时,也会发出这个通知)。
继续看下resyncToHardwareVsync函数:
- void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
- if (makeAvailable) {
- mHWVsyncAvailable = true;
- } else if (!mHWVsyncAvailable) {
- ALOGE("resyncToHardwareVsync called when HW vsync unavailable");
- return;
- }
- const nsecs_t period =
- getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
- mPrimaryDispSync.reset();
- mPrimaryDispSync.setPeriod(period);
- if (!mPrimaryHWVsyncEnabled) {
- mPrimaryDispSync.beginResync();
- mEventControlThread->setVsyncEnabled(true);
- mPrimaryHWVsyncEnabled = true;
- }
- }
这个函数是一个比较重要的函数。主要的功能就是打开HWC的VSync功能。
首先是获取HWC的信号发送间隔,设置给mPrimaryDispSync,然后重置mPrimaryDispSync。
如果之前没有使用HWC的VSYNC,那么还需要重新开始mPrimaryDispSync的计数,然后调用EventControlThread的setVsyncEnabled函数,我们前面已经讲过这个函数的作用——就是打开硬件的VSYNC功能—-我们前面提出了什么时候调用setVsyncEnabled函数疑问,现在这个疑问可以被回答了,这个函数是在屏幕点亮时被调用了。
init中VSync的简单小节
这样我们简单的学习完了init中和VSync有关的一些知识。其实主要的流程代码就是下面的一小段:
- {
- ...
- // start the EventThread
- sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- vsyncPhaseOffsetNs, true);
- mEventThread = new EventThread(vsyncSrc);
- sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- sfVsyncPhaseOffsetNs, true);
- mSFEventThread = new EventThread(sfVsyncSrc);
- mEventQueue.setEventThread(mSFEventThread);
- mEventControlThread = new EventControlThread(this);
- mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
- // set a fake vsync period if there is no HWComposer
- if (mHwc->initCheck() != NO_ERROR) {
- mPrimaryDispSync.setPeriod(16666667);
- }
- ...
- // initialize our drawing state
- mDrawingState = mCurrentState;
- // set initial conditions (e.g. unblank default device)
- initializeDisplays();
- ...
- }
- 首先就是创建了两个DispSyncSource和EventThread,这两组线程可以各自配置延迟,分别对应渲染和合成。这样渲染和合成就可以在收到真正的VSync信号之后错开执行。EventThread创建之后将会等待VSync信号的通知,一旦收到信号后将会分发给感兴趣的监听者。
- mEventQueue(MessageQueue)和EventThread建立连接,实际上就是注册成为了一个监听者。
- 创建了一个EventControlThread线程,用以和硬件HWC通信,控制HWC是否生成VSync信号。
- 最后mPrimaryDispSync.setPeriod(16666667)是指如果硬件不能生成VSync信号,那么软件将模拟生成,并且生成的周期设置为16.6ms左右。
5.最后在调用initializeDisplays函数初始化显示的过程中,我们又调用了onScreenAcquired函数,这个函数主要是唤醒了EventThread的等待,重置了mPrimaryDispSync,并且设置mPrimaryHWVsyncEnabled为true,并且通知硬件打开VSYNC通知。
基本流程如下图所示:
其中红色部分我们会在下一章VSync的接收和处理中再次展开讲解。
onVSyncReceived函数
这样,经过漫长的讲解,我们又从VSync的角度重新分析了SurfaceFlinger的init函数中的一些细节,现在我们终于可以回到这一小节开始之前我们提出的那几个问题:
当Surfaceflinger的收到VSync消息时的onVSyncReceived函数:
- void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
- bool needsHwVsync = false;
- {
- if (type == 0 && mPrimaryHWVsyncEnabled) {
- needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
- }
- }
- if (needsHwVsync) {
- enableHardwareVsync();
- } else {
- disableHardwareVsync(false);
- }
- }
这段代码很短,但是相当让人困惑,困惑至少有以下三点:
- mPrimaryHWVsyncEnabled是干嘛的,是被什么时候赋值的?
- enableHardwareVsync和disableHardwareVsync又起到了什么作用?
- mPrimaryDispSync是什么?addResyncSample有什么作用?
现在我们可以尝试来回答上面的这三个疑问了:
- mPrimaryHWVsyncEnabled是用来标识主屏幕对应的HWC的VSYNC功能有没有被开启。这个值在SurfaceFlinger创建时被赋值为false,在onScreenAcquired屏幕被点亮时,被设置为true。
- enableHardwareVsync所起的作用其实跟我们前面提到的resyncToHardwareVsync函数作用基本一样,也是打开硬件的VSync功能。而disableHardwareVsync自然就是关闭硬件的VSync功能。
- void SurfaceFlinger::enableHardwareVsync() {
- if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- mPrimaryDispSync.beginResync();
- mEventControlThread->setVsyncEnabled(true);
- mPrimaryHWVsyncEnabled = true;
- }
- }
- mPrimaryDispSync的类型是DispSync(当然这个变量的创建时刻依然有待确认),表示了一个基于硬件VSync信号的同步模型,它会根据从HWComposer来的硬件VSync信号的采样来进行同步。
了解了上面三个问题之后,我们终于可以来看下这段代码中最重要的一个函数调用—-addResyncSample。
我们将在下一章的VSync消息的处理一章中详细分析这个函数以及后续消息的处理流程。