Android SurfaceFlinger SW Vsync模型

Android SurfaceFlinger Vsync這塊比較復雜,最初在看這塊的時候,遲遲不知道從何入手,網上看了各種SurfaceFlinger Vsync相關的博客,個人感覺緊緊是把代碼流程給講了一遍,當涉及到更具體一些知識,比如updateModelLocked里的時間計算時都沒有一篇文章涉及到。

自己硬著頭皮看了好幾星期,稍微有些心得。所以在這里寫下博客將我所理解的SurfaceFlinger記錄下來

  • 一來是方便以后再回過頭來看時,
  • 一來也給其它讀者提供一個參考,利己利人。

本文代碼是基于 Android 7.0

轉載請標明來處: http://www.lxweimin.com/p/d3e4b1805c92

本文也是在參考了網上大牛的文章,自己加log debug后加上自己的理解寫的。下面推薦幾篇比較不錯的文章。

  • Android - SurfaceFlinger 之 VSync 概括
    這篇文章對 vsync 科普得還行, 沒有涉及到一行代碼。
  • Android 5.1 SurfaceFlinger VSYNC詳解
    這篇文章對 vsync 的傳遞流程講得還是挺不錯了,對于理解Surface Vsync流程還是不錯的。但是感覺僅僅是在分析代碼調用流程而已。
  • Android中的GraphicBuffer同步機制-Fence
    話說第一次見到Fence,也沒有仔細閱讀
  • DispSync
    這篇文章真的是五星推薦,它將SurfaceFlinger的Vsync機制最重要的DispSync部分拿出來講, 而且講得非常好。BTW, 這篇文章是我在網上搜到,覺得好像是我們現公司一個大牛寫的,于是跟他確認,結果真是他寫的。真是大牛。

一、SurfaceFlinger Vsync的線程圖

圖1 vsync信號產生

由圖1可以看出與vsync相關的SurfaceFlinger線程主要有以下幾個:

  1. EventControlThread: 控制硬件vsync的開關
  2. DispSyncThread: 軟件產生vsync的線程
  3. SF EventThread: 該線程用于SurfaceFlinger接收vsync信號用于渲染
  4. App EventThread: 該線程用于接收vsync信號并且上報給App進程,App開始畫圖

從這4個線程,其實我們可以將vsync分為4種不同的類型

  • HW vsync, 真實由硬件產生的vsync信號
  • SW vsync, 由DispSync產生的vsync信號
  • SF vsync, SF接收到的vsync信號
  • App vsync, App接收到的vsync信號

DispSync這篇文章里用了一個非常非常準確的 PLL 圖來表示上面4個vsync信號之間的關系。

圖2 DispSync的PLL模型

因此可以看出 SW vsync/App vsync 并不是直接由HW vsync產生的,而是由SW vsync產生的,HW vsync作為SW vsync的參考,動態的更新SW vsync里的模型參數,這樣讓SW vsync能與HW vsync更加的精確吧。

那么為什么SurfaceFlinger要用SW vsync而不是直接用HW vsync呢?
猜想可能是因為HW vsync每隔固定時間由顯示屏產生中斷,然后傳給driver, driver再回調給SurfaceFlinger, 這樣經過層層回調,會對performance有影響吧。而SW vsync直接由SurfaceFlinger產生,省略了很多步驟。

所以我個人覺得SurfaceFlinger最重要的是要搞明白 SW vsync是怎么運作的。

二、EventThread

為什么要先說EventThread? 很奇怪是吧,圖2 PLL圖 明明是SW vsync將vsync信號傳給 VSYNC-sf/VSYNC-app的,怎么還先講結果了呢?而不先講DispThread呢?

因為前面所說的4個線程互相影響,且是并行進行的,所以要想用一篇文章(單線程)來很順利的寫清楚(多線程的過程),而還要交待清楚前因后果,非常考驗這個作者的水平。所以第二節先說 EventThread 是為了寫好 DispSync 作鋪墊的。

由于SF EventThread和APP EventThread是同一套代碼, 而SF EventThread先運作起來,所以下面以SF EventThread為例作介紹.

2.1 EventThread的初始化

sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc,*this); 
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc, *this);
mEventQueue.setEventThread(mSFEventThread);

如上面所示,生成兩個EventThread,一個是APP EventThread, 一個是SF EventThread.
它們的區別在于相移phase offset不同,

EventThread 相移
App VSYNC_EVENT_PHASE_OFFSET_NS
SF SF_VSYNC_EVENT_PHASE_OFFSET_NS

這兩個值都可配,這兩個一般用來調節performance. 具體可在 BoardConfig.mk里配置

2.2 EventThread運行

void EventThread::onFirstRef() {
    run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
}

bool EventThread::threadLoop() {
    signalConnections = waitForEvent(&event);  //阻塞式的等待事件發生
    const size_t count = signalConnections.size();
    for (size_t i=0 ; i<count ; i++) {
        const sp<Connection>& conn(signalConnections[i]);
        status_t err = conn->postEvent(event);
    }
}

sp指針是生成對象結束后會調用onFirstRef.
接著又調用Thread的run函數,線程就一直開始反復調用threadLoop.
從threadLoop大致可以猜測出來,先等著事件發生(這里也就是vsync事件),然后將vsync事件分發出去,不同的EventThread(SF/APP EventThread)作的事情就開始不同了。

2.2.1接著看 waitForEvent()

Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
        DisplayEventReceiver::Event* event)
{
    Mutex::Autolock _l(mLock);
    Vector< sp<EventThread::Connection> > signalConnections;

    do {
        bool eventPending = false;
        bool waitForVSync = false;

        size_t vsyncCount = 0;
        nsecs_t timestamp = 0;

        for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
            timestamp = mVSyncEvent[i].header.timestamp;
            if (timestamp) {
                //如果這時從 mVSyncEvent里取得的timestamp大于0,表明這時已經有vsync事件待發送
                //從這里可以看出 mVSyncEvent就是保存VSYNC信號的變量,待后面分析
                // we have a vsync event to dispatch
                *event = mVSyncEvent[i];
                mVSyncEvent[i].header.timestamp = 0; //為什么要置為0呢??
                vsyncCount = mVSyncEvent[i].vsync.count;
                break;
            }
        }

        if (!timestamp) { 
            // no vsync event, see if there are some other event
            //沒有vsync事件, 來看下是否有其它pending的event, 這里主要是hotplug的事件
            eventPending = !mPendingEvents.isEmpty();
            if (eventPending) {
                // we have some other event to dispatch
                *event = mPendingEvents[0];
                mPendingEvents.removeAt(0);
            }
        }

        // find out connections waiting for events
        // mDisplayEventConnections保存的是注冊的Connection的,
       //  SF EventThread線程里只有一個Connection, 而這個Connection主要是用來渲染
        // 而如果是APP EventThread, 這里會有多個connection
        size_t count = mDisplayEventConnections.size();
        for (size_t i=0 ; i<count ; i++) {
            sp<Connection> connection(mDisplayEventConnections[i].promote());
            if (connection != NULL) {
                bool added = false;
              //這里的connection->count的值的大小有如下含義
             // count >= 1 : continuous event. count is the vsync rate  如果在大于等于1,表示會持續接收vsync event
            // count == 0 : one-shot event that has not fired    表示只接收一次
            // count ==-1 : one-shot event that fired this round / disabled   等于-1,表示不能再接收vsync事件了

                if (connection->count >= 0) {  //只能對還能接收的connection進行處理
                    // we need vsync events because at least
                    // one connection is waiting for it
                    waitForVSync = true;  //這個變量后面會用到
                    if (timestamp) {
                        // we consume the event only if it's time
                        // (ie: we received a vsync event)
                        if (connection->count == 0) {  //如定義一樣,如果是一次性的,那么在獲得本次vsync后,將它的count置為-1了, 下次只能通過 requestNextVsync 來重置為0
                            // fired this time around
                            connection->count = -1;
                            signalConnections.add(connection);  //最外層的while判斷條件會用到
                            added = true;
                        } else if (connection->count == 1 ||
                                (vsyncCount % connection->count) == 0) {
                            // continuous event, and time to report it
                            signalConnections.add(connection);
                            added = true;
                        }
                    }
                }
                
                if (eventPending && !timestamp && !added) {
                    // we don't have a vsync event to process
                  // 英文注釋已經寫的很明白了,如果此時沒有vsync事件,但是有pending的事件,那不管connection是否能接收了
                    // (timestamp==0), but we have some pending
                    // messages.
                    signalConnections.add(connection);
                }
            } else {
                // we couldn't promote this reference, the connection has
                // died, so clean-up!
                mDisplayEventConnections.removeAt(i);
                --i; --count;
            }
        }

        // Here we figure out if we need to enable or disable vsyncs
        if (timestamp && !waitForVSync) {
            // we received a VSYNC but we have no clients
            // don't report it, and disable VSYNC events
          // 英文注釋已經寫的很明白了,vsync事件已經發生了,但是我都還沒有client去監聽,那么這時你再繼續發vsync根本就是多余的
         // 所以直接disable Vsync, 注意這里并不是真正的disable硬件的VSYNC信號,見下面的分析
            disableVSyncLocked();
        } else if (!timestamp && waitForVSync) {
            // we have at least one client, so we want vsync enabled
            // (TODO: this function is called right after we finish
            // notifying clients of a vsync, so this call will be made
            // at the vsync rate, e.g. 60fps.  If we can accurately
            // track the current state we could avoid making this call
            // so often.)
        // 如果有client在監聽了,但是還沒有vsync事件,那么是否是之前vsync被disable了呢?
       //如果是的就要打開vsync監聽,
            enableVSyncLocked();
        }

        // note: !timestamp implies signalConnections.isEmpty(), because we
        // don't populate signalConnections if there's no vsync pending
        if (!timestamp && !eventPending) {//既沒有vsync事件,也沒有其它pending的事件(hotplug事件)
            // wait for something to happen
            if (waitForVSync) {  //但是有client在監聽了,這時就等著上報vsync事件即可
                // This is where we spend most of our time, waiting
                // for vsync events and new client registrations.
                //
                // If the screen is off, we can't use h/w vsync, so we
                // use a 16ms timeout instead.  It doesn't need to be
                // precise, we just need to keep feeding our clients.
                //
                // We don't want to stall if there's a driver bug, so we
                // use a (long) timeout when waiting for h/w vsync, and
                // generate fake events when necessary.
                bool softwareSync = mUseSoftwareVSync; //這里只考慮硬件vsync的情況,軟件模擬的暫時不考慮
                nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000);
              //如注釋所說的,如果是driver的bug,如果硬件一直不上報vsync事件怎么辦??難道就一直等下去??那client不就餓死了么?
             //所以這里如果driver不報vsync,那么就軟件模擬一個vsync事件,這里的timeout是1000ms,發一個
   
                if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) {
                    if (!softwareSync) {
                        ALOGW("Timed out waiting for hw vsync; faking it");
                    }
                    // FIXME: how do we decide which display id the fake
                    // vsync came from ?
                    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
                    mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
                    mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
                    mVSyncEvent[0].vsync.count++;
                }
            } else {
                // Nobody is interested in vsync, so we just want to sleep.
                // h/w vsync should be disabled, so this will wait until we
                // get a new connection, or an existing connection becomes
                // interested in receiving vsync again.
              //既沒有client, 又沒有硬件vsync事件,那么就死等下去了。
                mCondition.wait(mLock);
            }          
            
        }
    } while (signalConnections.isEmpty());

    // here we're guaranteed to have a timestamp and some connections to signal
    // (The connections might have dropped out of mDisplayEventConnections
    // while we were asleep, but we'll still have strong references to them.)
    return signalConnections;
}

對于這個函數的解釋已經基本上在注釋里已經寫得比較清楚了,下面來考慮最初的代碼運作過程,

  1. 函數第一次進入
    timestamp為0,即沒有vsync事件, 也沒有pending事件, 而且重要的是也沒有client,那么就直接進入 mCondition.wait(mLock) 死等

  2. 創建Connection
    當初始化完SF EventThread后,就開始創建SF Connection了。
    入口

mEventQueue.setEventThread(mSFEventThread);

創建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);
} 
sp<EventThread::Connection> EventThread::createEventConnection() const {
    return new Connection(const_cast<EventThread*>(this)); 
//這里注意了,初始化的Connection的count都為-1,即剛開始的時候,connection都不會接收vsync事件
}  

注冊Connection

void EventThread::Connection::onFirstRef() {
    // NOTE: mEventThread doesn't hold a strong reference on us
    mEventThread->registerDisplayEventConnection(this);
}
status_t EventThread::registerDisplayEventConnection(                                                                                                                 
        const sp<EventThread::Connection>& connection) {
    Mutex::Autolock _l(mLock);
    mDisplayEventConnections.add(connection);  
        //加入要SF EventThread里的mDisplayEventConnections里
    mCondition.broadcast();  //并釋放mCondition
    return NO_ERROR;
}
  1. 第2步中mCondition.broadcast()會喚醒第一步中的mCondition.wait(),但是在waitForEvent的while循環為false,再做while一次循環
  2. 這時候 timestamp還是為0,還是沒有pending的event, 但是這時有SF的connection了,只不過此時connection的count仍然為默認的-1,
  3. 最后還是進入 mCondition.wait死等.

注意: 實際在調試的時候 registerDisplayEventConnection會比SF EventThread的threadLoop先運行起來,不過最后的結果是一樣的。

由第4步可知Connection的初始化count為-1,即表示該Connection不會接收vsync事件,那么這個值是在什么地方被修改的呢?

答案是在SurfaceFlinger初始化的最后initializeDisplays里

2.2.2 requestNextVsync

initializeDisplays();
    flinger->onInitializeDisplays();
        setTransactionState(state, displays, 0);
            setTransactionFlags(transactionFlags);
                signalTransaction();
                    EventQueue.invalidate();
                        mEvents->requestNextVsync()  //mEvents是Connection實例
                            EventThread->requestNextVsync(this);

requestNextVsync表示主動去請求獲得vsync事件, 上面的意思是將Display初始化后,即顯示屏可以工作后,那么SF EventThread就開始要監聽vsync事件了。

void EventThread::requestNextVsync(
        const sp<EventThread::Connection>& connection) {
    Mutex::Autolock _l(mLock);

    mFlinger.resyncWithRateLimit();

    if (connection->count < 0) {
        connection->count = 0;  //這里將SurfaceFlinger的Count改為0,變成一次性接收的了
        mCondition.broadcast();  //釋放EventThread里的mCondition
    }
}                       
  • a) requestNextVsync釋放EventThread里的mCondition后,接著會喚醒 EventThread里的上面第5步的mCondition.wait, 這時會再走一遍while循環
  • b). 這時候timestamp還是為0,還是沒有pending的event, 但是這時有SF的connection了, 且此時的connection的count已經被置為了0,表明此時有connection在監聽了,即waitForVSync為true
  • c) 接下來 enableVSyncLocked
  • d) 進入mCondition.waitRelative(), 其中超時時間為1000ms

那么 enableVSyncLocked 這個函數又是干什么的呢?

2.2.3 enableVSyncLocked

void EventThread::enableVSyncLocked() {
    if (!mUseSoftwareVSync) {
        // never enable h/w VSYNC when screen is off
        if (!mVsyncEnabled) { //這里只考慮硬件vsync的情況,而不考慮軟件模擬的情況
            mVsyncEnabled = true; 
            mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
            mVSyncSource->setVSyncEnabled(true);
        }
    }
    mDebugVsyncEnabled = true;
    sendVsyncHintOnLocked();
}

這里只考慮硬件vsync的情況,即mUseSoftwareVSync為false的情況,最后調用到 setVsyncEnabled, 且其值為true

virtual void setVSyncEnabled(bool enable) {
    Mutex::Autolock lock(mVsyncMutex);
    if (enable) {
      // 將EventListener最終加入到DispSyncThread的mEventListeners里
        status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                static_cast<DispSync::Callback*>(this));
        if (err != NO_ERROR) {
            ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err);
        }    
        //ATRACE_INT(mVsyncOnLabel.string(), 1);
    } else {
      //相反如果 enable 為false時,那么就從EventListeners里刪除掉
        status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this));
        if (err != NO_ERROR) {
            ALOGE("error unregistering vsync callback: %s (%d)",strerror(-err), err);
        }    
        //ATRACE_INT(mVsyncOnLabel.string(), 0);
    }    
    mEnabled = enable;
} 
status_t addEventListener(const char* name, nsecs_t phase,
        const sp<DispSync::Callback>& callback) {
    if (kTraceDetailedInfo) ATRACE_CALL();
    Mutex::Autolock lock(mMutex);

    for (size_t i = 0; i < mEventListeners.size(); i++) {
        if (mEventListeners[i].mCallback == callback) {
            return BAD_VALUE;
        }
    }

    EventListener listener;
    listener.mName = name;
    listener.mPhase = phase;
    listener.mCallback = callback;

    // listener里的mLastEventTime這個在這里初始化的意義是防止之前的VSYNC事件被發送出去了
    // We want to allow the firstmost future event to fire without
    // allowing any past events to fire
    listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency;

    mEventListeners.push(listener);
   //DispSyncThread的 mCond釋放
    mCond.signal();

    return NO_ERROR;
}

第二節主要是為DispSyncThread添加EventListener, 那下面這節就是為DispSyncThread設置Peroid. 這樣DispSync模型就可以動作起來了。

三、開關硬件HWC

在SurfaceFlinger初始化Display后,會調用resyncToHardwareVsync跟硬件vsync進行同步

initializeDisplays();
    flinger->onInitializeDisplays();
        setPowerModeInternal()
            resyncToHardwareVsync(true);
                repaintEverything();

3.1 resyncToHardwareVsync函數

void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
    Mutex::Autolock _l(mHWVsyncLock);

    if (makeAvailable) {
        // mHWVsyncAvailable表示HW vsync被enable了
        mHWVsyncAvailable = true;
    } else if (!mHWVsyncAvailable) {
        // Hardware vsync is not currently available, so abort the resync
        // attempt for now
        return;
    }

   //獲得顯示設備的刷新率,比如60HZ, 那么period就是16.6667ms,即每隔16.6667就會產生一個硬件vsync信號
    const nsecs_t period =
            getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
    //當前這個值跟具體的顯示設備有關,并不一定是60HZ
    mPrimaryDispSync.reset();
    //設置DispSync模型里period為顯示設備的頻率
    mPrimaryDispSync.setPeriod(period);

   //mPrimaryHWVsyncEnabled表示當前的硬件vsync是否enable,
    if (!mPrimaryHWVsyncEnabled) {
        mPrimaryDispSync.beginResync();
        //如果硬件vsync沒有enable,那么就通知EventControlThread去通知硬件enable VSYNC,這個和DispSync的setVsyncEnabled是不一樣的
        mEventControlThread->setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}

3.2 setPeriod 更新mPeriod

mPrimaryDispSync.setPeriod(period);
void DispSync::setPeriod(nsecs_t period) {
    Mutex::Autolock lock(mMutex);
    mPeriod = period;
    mPhase = 0;
    mReferenceTime = 0;
    mThread->updateModel(mPeriod,mPhase,mReferenceTime);                                                                                                        
}

mPeriod表示具體的硬件產生vsync的時間間隔
mThread是DispSyncThread, DispSync在初始化的時候直接生成一個線程DispSyncThread并運行起來

void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {                                                                                          
    if (kTraceDetailedInfo) ATRACE_CALL();
    Mutex::Autolock lock(mMutex);
    mPeriod = period;
    mPhase = phase;
    mReferenceTime = referenceTime;
    mCond.signal(); 
}

updateModel里會再次喚醒 DispSyncThread的里的 mCond, 注意此時 mPeroid已經不為0了。

四、硬件Vsync的控制

4.1 默認開閉硬件vsync

SurfaceFlinger在初始化HWComposer時會默認關閉硬件Vsync信號,這里直接調用eventControl.
具體代碼如下

HWComposer::HWComposer() {
    eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
}

void HWComposer::eventControl(int disp, int event, int enabled) {
    err = mHwc->eventControl(mHwc, disp, event, enabled);
}

mHwc是hwc_composer_device_1類型,它表示是對一個硬件設備的抽象吧,通過它就可以控制和使用硬件相關功能吧。

那么硬件的Vsync是在什么時候被打開的呢?

4.2 打開硬件vsync

具體是在3.1 resyncToHardwareVsync 函數最后的代碼打開的。
resyncToHardwareVsync函數從字面上看來就是和硬件的Vsync進行同步的意思。

    if (!mPrimaryHWVsyncEnabled) {
        mPrimaryDispSync.beginResync();
        //如果硬件vsync沒有enable,那么就通知EventControlThread去通知硬件enable VSYNC,
        //這個和DispSync的setVsyncEnabled是不一樣的
        mEventControlThread->setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }

resyncToHardwareVsync函數通過EventControlThread去控制硬件Vsync信號的開關

void EventControlThread::setVsyncEnabled(bool enabled) {                                                                                                              
    Mutex::Autolock lock(mMutex);
    mVsyncEnabled = enabled; // mVsyncEnabled一個控制開關
    mCond.signal();  //釋放EventControlThread里的mCond信號
}

setVsyncEnabled會釋放mCond信號,這樣在EventControlThread的threadLoop里的mCond會被喚醒去操作硬件Vsync開關了

好了,經過三節的鋪墊終于可以說下DispSync.

五、DispSync模型

DispSync 是定義在SurfaceFlinger類里的成員變量,因此在初始化 SurfaceFlinger時,就會初始化DispSync, 它在SurfaceFlinger里的具體定義是

DispSync mPrimaryDispSync

而DispSync在初始化的時候會生成 DispSyncThread 線程,緊接著將 DispSyncThread run起來,根據C++ Thread模型, DispSyncThread 會循環調用threadLoop() 函數。

下面來看下 DispSyncThread 里的 threadLoop()函數, 之所以把它的所有代碼粘貼上來,是方便以后回顧之用。

5.1 DispSync模型運作

5.1.1 等待可用的EventListener

virtual bool threadLoop() {
    status_t err;
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 
    //獲得當前的系統時間,這個是比較老的時間了

    while (true) {
        Vector<CallbackInvocation> callbackInvocations;

        nsecs_t targetTime = 0;

        { // Scope for lock
            Mutex::Autolock lock(mMutex);

            if (kTraceDetailedInfo) {
                ATRACE_INT64("DispSync:Frame", mFrameNumber);
            }
            ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
            ++mFrameNumber; 
           //mFrameNumber僅僅是一個計數而已,沒有實際用處,它和vsync個數是不等同的

            if (mStop) {
                return false;
            }
           
            //當threadLoop第一次進來后,由于mPeriod初始化為0,所以一直死等在這里
            if (mPeriod == 0) {
                err = mCond.wait(mMutex);  // **blockingA**
                if (err != NO_ERROR) {
                    ALOGE("error waiting for new events: %s (%d)",  strerror(-err), err);
                    return false;
                }
                continue;
            }
          ...
    }
}

<a name="dispsync_firstrun"></a>當threadLoop第一次運行,mPeriod初始化為0,所以一直死等在"blockingA"處。

5.1.2 往DispSyncThread里加入EventListener

具體是在 setVSyncEnabled里,參考 2.2.3 enableVSyncLocked
setVSyncEnabled 將 EventListener添加到 DispSync 里的mEventListeners里,然后釋放mCond.signal(), 繼而mCond會喚醒 5.1.1 中的 blockingA.

注意此時 mPeriod 依然為0,所以線程也一直死等在"blockingA" 處, 但是DispSyncThread的 mEventListeners 已經加入了listener了。

5.1.3 DispSyncThread收到mPeriod更新

由5.1.2可知,由于mPeriod為0,所以線程一直死等在blockingA處,
而由3.2 setPeriod可知,此時mPeriod已經被更新成顯示設備的刷新率了,且 mCond已經被釋放了,因此 blockingA mCond.wait()被喚醒了。

這時進入threadLoop的第二階段,計算下一個Vsync信號的時間戳,并且上報給EventListener. 就這樣,DispSyncThread模型就運作起來了。

5.2 更新DispSync模型

由 4.2 小節可知,硬件Vsync已經在resyncToHardwareVsync被打開了,既然打開了,那么只要有硬件Vsync信號產生,就可回調 hook_vsync函數(hook_vsync函數在HWComposer的初始化的時候被注冊的)

5.2.1 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);
}

具體調用到HWComposer的vsync

void HWComposer::vsync(int disp, int64_t timestamp) {
    if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
        {
            Mutex::Autolock _l(mLock);

            // 防止重復上報相同的vsync
            // There have been reports of HWCs that signal several vsync events
            // with the same timestamp when turning the display off and on. This
            // is a bug in the HWC implementation, but filter the extra events
            // out here so they don't cause havoc downstream.
            if (timestamp == mLastHwVSync[disp]) {
                ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")", timestamp);
                return;
            }

            mLastHwVSync[disp] = timestamp;
        }

        char tag[16];
        snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
        ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
        //回調 onVsyncReceived函數
        mEventHandler.onVSyncReceived(disp, timestamp);
    }
}

5.2.2 onVsyncReceived

void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
    bool needsHwVsync = false;

    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        // 這里的type為0,表示的是primary display, 
        // 而 mPrimaryHWVsyncEnabled 在最初的resyncToHardwareVsync里已經被設置為true了,
        // 所以這里會進入addResyncSample
        if (type == 0 && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
        }    
    }    
    //addResyncSample會根據現有的硬件Vsync樣本計算SW Vsync模型,如果誤差已經在可接受范圍內
   // 即認為不再需要硬件Vsync樣本了,就得關閉硬件Vsync
   // 反之,如果誤差還比較大,這里還需要繼續加入硬件Vsync樣本繼續計算SW Vsync模型 
   // enableHardwareVsync/disableHardwareVsync都是通過EventControlThread去控制硬件Vsync開關
    if (needsHwVsync) {
        enableHardwareVsync();               
    } else {
        disableHardwareVsync(false);
    }    
}

5.2.3 addResyncSample

addResyncSample函數從字面上來講就是加入硬件vsync的樣本,目的是為了計算更新SW Vsync里面的參數。 具體的解釋全部以注釋的方式寫在代碼里了。

bool DispSync::addResyncSample(nsecs_t timestamp) {                                                                                                                   
    Mutex::Autolock lock(mMutex);

     //這里MAX_RESYNC_SAMPLES為32,即最大只保存32次硬件vsync時間戳,用來計算SW vsync模型.
      // mNumResyncSamples 表示已經有多少個硬件vsync 樣本了 
     // 如果 mNumResyncSamples 等于32個了,那么下一次vsync來了,就用 mFirstResyncSample來記錄是第幾個
    // 如果保存的vsync個數達到最大32個的時候, 這樣 mNumResyncSamples 和
    // mFirstResyncSample 兩個變量就組成一個窗口(長度為32)向前滑動, 
    // 在滑動過程中丟掉最老的硬件vsync樣本
    size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
    // mResyncSamples 記錄每個硬件vsync樣本的時間戳,在計算sw vsync的模型時有用
    mResyncSamples[idx] = timestamp;
    
    //如果是第一個硬件vsync樣本,就直接更新模型 (注意,這里的第一個硬件vsync并不是指開機后的第一個vsync, 
    //而是指 mNumResyncSamples被清0后的第一個vsync信號),具體在是beginResync里清0的
    //這里提前說一下,當SW Vsync與硬件Vsync誤差比較大后,要重新校準,這里就要 beginResync,
    //它主要是重置一些值 ,比如 mNumResyncSamples, 既然有誤差了,那么之前保存的硬件vsync樣本就不能用了,就重新保存新的硬件vsync樣本來調節精度了
    //所這里也很好理解,首先讓SW Vsync模型以第一個硬件vsync為基準(注意第一個硬件vsync的含義),然后再慢慢調節它的精度
        
    if (mNumResyncSamples == 0) {   
        mPhase = 0;
        mReferenceTime = timestamp;  //參考時間設置為第一個硬件vsync的時間戳
        mThread->updateModel(mPeriod, mPhase, mReferenceTime);
    }   

    //更新 mNumResyncSamples 或 mFirstResyncSample的值
    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
        mNumResyncSamples++;
    } else {
        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
    }   

    // 開始計算更新SW vsync 模型
    updateModelLocked();

    //如果 mNumResyncSamplesSincePresent 大于4,重置 Error信息
    // mNumResyncSamplesSincePresent 表示的是當目前的硬件 vsync samples個數大于4個時,就重置error信息。
    // 注意,在硬件vsync被enable的條件下fence是無效的,所以在這里需要將error信息清空,
    // 但是為什么要大于MAX_RESYNC_SAMPLES_WITHOUT_PRESENT(4)時才去reset error信息呢?
    //注意: 當mNumResyncSamplesSincePresent大于4時,意味著已經保存有6個硬件Vsync樣本了,自己好好算算,
    //由于在硬件Vsync在enable時fence無效,那么應該是每來一個硬件Vsync就應該要reset error呀?為啥還要等到6個過后才reset呢?
    //確實是這樣的,但是在updateModelLocked中,要更新SW vsync模型,至少得有6個及以上的樣本才行,所以至少要有6個硬件vsync樣本,
    //所以fense在前6個硬件vsync樣本都是無效的,因此不必每次都reset,只要它大于6個過后再reset,真的是細思極恐啊。
    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
        resetErrorLocked();
    }   

    if (kIgnorePresentFences) {
        // If we don't have the sync framework we will never have
        // addPresentFence called.  This means we have no way to know whether
        // or not we're synchronized with the HW vsyncs, so we just request
        // that the HW vsync events be turned on whenever we need to generate
        // SW vsync events.
        return mThread->hasAnyEventListeners();
    }   
    // Check against kErrorThreshold / 2 to add some hysteresis before having to
    // resync again
    // 如果模型更新了,并且產生的錯誤小于 kErrorThreshold/2 這個值 (這個值是錯誤容忍度),那么 modelLocked就被置為true, 即模型被鎖定,模型被鎖定的含義是
    // 現在SW vsync工作的很好,暫時不需要硬件Vsync來進行校正了,最后會將硬件Vsync給disable掉
    bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
    ALOGV("[%s] addResyncSample returning %s", mName,
            modelLocked ? "locked" : "unlocked");
    return !modelLocked;
}

接下來繼續看下是怎樣更新模型里的參數的

5.2.4 updateModelLocked更新模型參數

updateModelLocked函數是根據已經保存的硬件Vsync樣本來計算模型的參數。

void DispSync::updateModelLocked() {
    // 如果已經保存了6個以上的 硬件 vsync 樣本后,就要開始計算 sw vsync模型了
    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
        nsecs_t durationSum = 0;
        nsecs_t minDuration = INT64_MAX;
        nsecs_t maxDuration = 0;
        //還記得上面 如果 mNumResyncSamples=0,即第一個硬件vsync時,直接更新SW vsync模型了,所以這里把第一個給去除掉
        for (size_t i = 1; i < mNumResyncSamples; i++) {
            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
            size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
            
            // mResyncSamples[idx] - mResyncSamples[prev] 這個差值就是計算出兩個硬件vsync樣本之間的時間間隔
            nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
            // durationSum 表示保存的所有樣本(除去第一個vsync)時間間隔之后,用于后面計算 平均 mPeriod
            durationSum += duration;  
            minDuration = min(minDuration, duration);
            maxDuration = max(maxDuration, duration);
        }

        // 去掉一個最小,一個最大值再來計算平均值,這個平均值就是硬件vsync產生的時間間隔
        // Exclude the min and max from the average
        durationSum -= minDuration + maxDuration;
        // 這里減去3是 一個最大,一個最小,還有第一個硬件vsync
        mPeriod = durationSum / (mNumResyncSamples - 3);

       //下面計算出模型需要的偏移, 因為現在 mPeriod 算出來的是平均值,所以并不是真的硬件vsync時間間隔就是 mPeriod, 存在著偏移與噪音(這個和樣本個數有很大的關系)
       // 即有些樣本信號的時間間隔大于平均值,而有些樣本時間間隔小于平均值,而這些與 mPriod的差值就是偏移
       // 下面就是要算出這些平均的偏移值
        double sampleAvgX = 0;
        double sampleAvgY = 0;
        //將硬件vsync的時間間隔換算成對應的度數,即刻度,這里的刻度表示每ns代表多少度 
        double scale = 2.0 * M_PI / double(mPeriod);
        // Intentionally skip the first sample
        //同樣去掉第一個樣本
        for (size_t i = 1; i < mNumResyncSamples; i++) {
            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
            nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
           // 這里對mPeriod取余就是相對于mPeriod倍數的偏移值,然后將其轉換成對應的度數
            double samplePhase = double(sample % mPeriod) * scale;
            sampleAvgX += cos(samplePhase); //依次累加成 sampleAvgX
            sampleAvgY += sin(samplePhase); //依次累加成 sampleAvgY
        }

        //獲得在x軸與y軸的偏移的平均值
        sampleAvgX /= double(mNumResyncSamples - 1);
        sampleAvgY /= double(mNumResyncSamples - 1);

       //最后再通過atan2獲得最終的相移值
        mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);

        //如果相移偏過了mPeriod的一半,那么重新調整一下
        if (mPhase < -(mPeriod / 2)) {
            mPhase += mPeriod;
            ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
        }

        if (kTraceDetailedInfo) {
            ATRACE_INT64("DispSync:Period", mPeriod);
            ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
        }

        // 這個 mRefreshSkipCount 一般為0,它的意思是多少個vsync才進行刷新,即人為的降低顯示設備的刷新率了
       // mRefreshSkipCount 通過 setRefreshSkipCount來設置
        // Artificially inflate the period if requested.
        mPeriod += mPeriod * mRefreshSkipCount;

      // 將最新的 偏移 mPhase和 vsync時間間隔mPeriod和mReferenceTime更新到SW vsync模型當中
        mThread->updateModel(mPeriod, mPhase, mReferenceTime);
        
      // 模型更新了
        mModelUpdated = true;
    }
}

下面來看下幾個比較重要的變量

  1. 硬件vsync樣本個數 MIN_RESYNC_SAMPLES_FOR_UPDATE
    要6個硬件vsync樣本以上才計算,當然樣本越多,模型越精確
  2. mPeriod
    即是顯示屏的刷新率,這里mPeriod是根據樣本個數去掉一個最大一個最小,算平均
  3. mPhase
    這個是偏移移時間,這個相稱和具體的SF/APP Thread里固定的相稱是不一樣的,這個相移是針對 mPeroid的一個偏移。
  4. mModelUpdated
    這個bool變量表示是否模型已經更新了
  5. mReferenceTime
    這個是第一個硬件Vsync的時間,每次SW vsync計算下一個vsync時間時,都是以該時間作為基準,這樣可以減少誤差。
    為什么不以上一個SW vsync時間為基準呢?
    想像一下,如果SW vsync的每一個Vsync都以上一個vsync時間作為基準,那相當于誤差就會不停的累加,而如果以第一個硬件vsync時間作基準,那每次vsync的誤差是不會累加的。
圖3 相移圖
        //將硬件vsync的時間間隔換算成對應的度數,即刻度,這里的刻度表示每ns代表多少度 
        double scale = 2.0 * M_PI / double(mPeriod);
        // Intentionally skip the first sample
        //同樣去掉第一個樣本
        for (size_t i = 1; i < mNumResyncSamples; i++) {
            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
            nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
           // 這里對mPeriod取余就是相對于mPeriod倍數的偏移值,然后將其轉換成對應的度數
            double samplePhase = double(sample % mPeriod) * scale;
            sampleAvgX += cos(samplePhase); //依次累加成 sampleAvgX
            sampleAvgY += sin(samplePhase); //依次累加成 sampleAvgY
        }

        //獲得在x軸與y軸的偏移的平均值
        sampleAvgX /= double(mNumResyncSamples - 1);
        sampleAvgY /= double(mNumResyncSamples - 1);

       //最后再通過atan2獲得最終的相移值
        mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
圖4 相移計算公式

mPhase對應的角度Angle是通過atan2(sampleAvgY, sampleAvgX)計算出來的,
最后將角度/scale即可得到相移,單位也是納秒.

5.2.3 計算SW vsync下一個vsync時間點

上面已經介紹了DispSync模型了,且模型已經更新好了,那就可以由SW vsync發出vsync信號了呀。
那接著5.1.1 DispSyncThread的threadLoop的下半部分代碼分析

virtual bool threadLoop
{
        ...
    //計算下一次vsync事件的時間
    targetTime = computeNextEventTimeLocked(now);

    bool isWakeup = false;

    //如果計算出來的下一次vsync事件還沒有到來,那就等著唄,等著時間到了,就發送SW VSYNC信號
    //可以看出 DispSyncThread的發送的vsync信號和真正硬件發生的vsync信號沒有直接的關系,
       //發送給app/sf的vsync信號都是由 DispSyncThread發送出去的.
    if (now < targetTime) {
        if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting");

        if (targetTime == INT64_MAX) {
            err = mCond.wait(mMutex);
        } else {
                        //等著SW VSYNC時間到了,就喚醒,開始發送vsync信號
            err = mCond.waitRelative(mMutex, targetTime - now);
        }

        if (err == TIMED_OUT) {
                        //mCond 是自己醒的,即在targetTime-now時間后醒來的,那就要計算wake up的時間
            isWakeup = true;
        } else if (err != NO_ERROR) {
            ALOGE("error waiting for next event: %s (%d)",
                    strerror(-err), err);
            return false;
        }
    }
    
    now = systemTime(SYSTEM_TIME_MONOTONIC);

     //計算wake up時間, 但是不能超過1.5 ms
    // Don't correct by more than 1.5 ms
    static const nsecs_t kMaxWakeupLatency = us2ns(1500);

    if (isWakeup) {
        // mWakeupLatency 醒來時間是累加的,這個在后面計算SW vsync的時間有用, 不過所有的wake up時間最大不能超過1.5 ms, 這點延遲就是代碼上的延遲了,看來Google計算的很嚴謹呀
        mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64; 
        mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
        if (kTraceDetailedInfo) {
            ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
            ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
        }   
    }   

    //收集回調的EventListener, 注意,前面已經加入了eventlistener,參見5.1.2 所以callbackInvocations.size()肯定大于0
    callbackInvocations = gatherCallbackInvocationsLocked(now);
    
    if (callbackInvocations.size() > 0) {
        //向SF/APP EventThread發送Vsync信號                                                                                                           
        fireCallbackInvocations(callbackInvocations);
    }
}

接著來看下SW vsync模型是怎樣計算vsync時間的呢

nsecs_t computeNextEventTimeLocked(nsecs_t now) {
    if (kTraceDetailedInfo) ATRACE_CALL();
    nsecs_t nextEventTime = INT64_MAX;
    //對所有的EventListener進行分別計算,里面的mLastEventTime值不同
    for (size_t i = 0; i < mEventListeners.size(); i++) {
        nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],now);
        if (t < nextEventTime) {
            nextEventTime = t;
        }
    }
    return nextEventTime;
}

這里其實就最多只有兩種EventListener, 一個是SF EventThread,一個是App EventThread,它們都需要接收Vsync信號來分別做不同的事情。
但是實際上兩個線程都有一個偏移,見2.1,它們工作既保持一定的節拍,又可以相互錯開,一前一后保持著咚次噠次, 還可以讓CPU能錯開工作高峰。
Android 5.1 SurfaceFlinger VSYNC詳解

圖5 Vsync phase
nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
        nsecs_t baseTime) {
    if (kTraceDetailedInfo) ATRACE_CALL();
    
    // lastEventTime 是求的是上一次vsync事件的時間,它等于上一次vsync事件加上wake up時間
    // 一般來說baseTime應該不會小于 lastEventTime
   // 也有小于的情況,比如第一次,threadLoop的now生成的時間比較早,而 addEventListener 發生的比較晚。
   // 而listener的lastEventTime設為了當前的系統時間,這時baseTime 就會小于 lastEventTime
    nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
    if (baseTime < lastEventTime) {
        //重新修正 baseTime
        baseTime = lastEventTime;
    }
    // baseTime 減去參考的時間,這個 mReferenceTime就是第一個硬件Vsync樣本的時間
    baseTime -= mReferenceTime;
    // phase偏移, mPhase是通過硬件vsync的樣本計算出來的,而listener.mPhase是固定的具體是在編譯時設置的
    // sf 使用的是 SF_VSYNC_EVENT_PHASE_OFFSET_NS;
    //而APP使用的VSYNC_EVENT_PHASE_OFFSET_NS
    nsecs_t phase = mPhase + listener.mPhase;
    
   // 減去偏移
    baseTime -= phase;

    // If our previous time is before the reference (because the reference
    // has since been updated), the division by mPeriod will truncate
    // towards zero instead of computing the floor. Since in all cases
    // before the reference we want the next time to be effectively now, we
    // set baseTime to -mPeriod so that numPeriods will be -1.
    // When we add 1 and the phase, we will be at the correct event time for
    // this period.
    if (baseTime < 0) {
        baseTime = -mPeriod;
    }

   //下面是求出下一時刻發送 sw vsync的時間,這個時間是以第一個硬件vsync作為參考來這樣計算
   //為什么不是以上一個sw vsync時間作為參考呢?為什么要以第一個硬件vsync時間作為參考呢?
   //如果以一個sw vsync時間作為參考,因為sw vsync的時間本身就是一種根據模型模擬出來的,所以本身存在誤差,所以如果每個sw vsync以上一個作為base的話,
   //那么它的誤差會慢慢積累。
   //而每次以第一個硬件vsync時間作為基準,那么每個sw vsync的誤差,并不會累加,這樣就相對來說更加精確些
    nsecs_t numPeriods = baseTime / mPeriod;
   //算出距離第一個硬件Vsync時間的偏移,即得到下一個sw vsync的時間,numPeriods + 1,注意是下一個vsync的時間
    nsecs_t t = (numPeriods + 1) * mPeriod + phase;
   // 這個時間t是相對于每一個硬件 vsync的時間
    t += mReferenceTime;

    // 如果這個vsync距離上一個vsync時間小于3/5個mPeriod的話,為了避免連續的兩個sw vsync, 那么這次sw vsync就放棄了,直接放到下一個周期里
    // Check that it's been slightly more than half a period since the last
    // event so that we don't accidentally fall into double-rate vsyncs
    if (t - listener.mLastEventTime < (3 * mPeriod / 5)) {
        t += mPeriod;
    }

  // 當然算出來的時間要減去wake up的時間了,這樣才能精確的模擬硬件vsync的時間, 注意 mWakeupLatency 是所有wake up的時間累加,但是最大只能到1.5ms
    t -= mWakeupLatency;

    return t;
}

繼續看下 gatherCallbackInvocationsLocked

Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
    if (kTraceDetailedInfo) ATRACE_CALL();
    ALOGV("[%s] gatherCallbackInvocationsLocked @ now %" PRId64, mName,
            ns2us(now));

    Vector<CallbackInvocation> callbackInvocations;
    //因為computeListenerNextEventTimeLocked計算的是下一個vsync時間,那么這一次的vsync就以上now - mPeriod作為基準時間
    nsecs_t onePeriodAgo = now - mPeriod;

    for (size_t i = 0; i < mEventListeners.size(); i++) {                                                                                                         
        nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
                onePeriodAgo);

        if (t < now) {
            CallbackInvocation ci; 
            ci.mCallback = mEventListeners[i].mCallback;
            ci.mEventTime = t;
            callbackInvocations.push(ci);
            //記錄SW vsync的時間
            mEventListeners.editItemAt(i).mLastEventTime = t;
        }   
    }   

    return callbackInvocations;
} 

到這里基本上說完了DispSync更新模型,以及計算SW Vsync時間。那到這里完了么?還沒有吶,現在SW vsync已經按需要由DispSync發出了,但這就完全和硬件Vsync信號保持一致了么?還不一定,所以還需要看下SW vsync與硬件Vsync之間的誤差是否還在可接收范圍內。

5.2.4 更新SW Vsync的誤差值

SurfaceFlinger在收到SW Vsync信號后就要去渲染,做圖像的合成,在渲染完后會調用postComposition函數,

5.2.4.1 postComposition

void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
{
    mAnimFrameTracker.setPostCompositionTime(mPostCompositionTimestamp);
    const LayerVector& layers(mDrawingState.layersSortedByZ);
    const size_t count = layers.size();
    for (size_t i=0 ; i<count ; i++) {
        layers[i]->onPostComposition(mPostCompositionTimestamp);
    }    

    // 通過 HWComposer 獲得 Fence
    const HWComposer& hwc = getHwComposer();
    sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);

    //注意,如果硬件vsync已經被打開了,那么fence是無效了,只有它在關閉的情況下,它才有效
    if (presentFence->isValid()) {
        if (mPrimaryDispSync.addPresentFence(presentFence)) {
            ALOGD("in setPostCompositionTime will enableHardwareVsync");
            enableHardwareVsync();
        } else {
            disableHardwareVsync(false);
        }    
    }    
}

5.2.4的updateModelLocked函數可知,當更新SW Vsync模型后,就會關閉硬件Vsync信號,這時候Fence就有效了, 對于 Fence, 可以參考Android中的GraphicBuffer同步機制-Fence, 這里簡單的理解就是拿到真實硬件Vsync的狀態,包含硬件Vsync發生的時間.

5.2.4.2 addPresentFence

bool DispSync::addPresentFence(const sp<Fence>& fence) {
    Mutex::Autolock lock(mMutex);

   // 將當前硬件vsync的fence保存在 mPresentFences里, 目的是為了計算偏移
  // mPresentFences 最多保存8個硬件 偏移
    mPresentFences[mPresentSampleOffset] = fence;
    mPresentTimes[mPresentSampleOffset] = 0; 
    mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
    mNumResyncSamplesSincePresent = 0; // 將 mNumResyncSamplesSincePresent 置為0,

    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
        const sp<Fence>& f(mPresentFences[i]);
        if (f != NULL) {  //這里 f 是有可能為NULL, 即只有一個 硬件 vsync 偏移時
            nsecs_t t = f->getSignalTime();  //猜測這個就是硬件 vsync的時間
            if (t < INT64_MAX) {
                mPresentFences[i].clear();
              //將每個vsync時間戳記錄在 mPresentTimes 里,這里 kPresentTimeOffset是可以配置的,即可調的
                mPresentTimes[i] = t + kPresentTimeOffset;
            }
        }
    }
   //更新錯誤信息
    updateErrorLocked();

    // 這里,一般的情況是 mModelUpdated 已經被更新了,然后硬件vsync被disable了,
    // 所以這里只需要看SW vsync的真實的硬件vsync的誤差是否在可
    // 允許的范圍內即可
    return !mModelUpdated || mError > kErrorThreshold;
}

addPresentFence最后的返回, mError是方差,見下面5.2.4.3分析,當方差大于 kErrorThreshold后就返回true

return !mModelUpdated || mError > kErrorThreshold;

5.2.4.3 updateErrorLocked

void DispSync::updateErrorLocked() {
    if (!mModelUpdated) {
        return;
    }

    // Need to compare present fences against the un-adjusted refresh period,
    // since they might arrive between two events.
    
    //得到真實的 period, 具體見 5.2.4 updateModelLocked 里的分析
    nsecs_t period = mPeriod / (1 + mRefreshSkipCount);

    int numErrSamples = 0;
    nsecs_t sqErrSum = 0;

   //這里的 mReferenceTime 是第一個硬件vsync的時間戳 見 addResyncSample里的 mReferenceTime
    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
        nsecs_t sample = mPresentTimes[i] - mReferenceTime;
       // 這里 sample 一般來說是大于偏移的
        if (sample > mPhase) {
            nsecs_t sampleErr = (sample - mPhase) % period;
            if (sampleErr > period / 2) {
                sampleErr -= period;
            }
            //記錄 偏移差的平方和
            sqErrSum += sampleErr * sampleErr;
            numErrSamples++;
        }
    }

     // 說到底mError就是方差
    if (numErrSamples > 0) {
        mError = sqErrSum / numErrSamples;
    } else {
        mError = 0;
    }

    if (kTraceDetailedInfo) {
        ATRACE_INT64("DispSync:Error", mError);
    }
}

5.2.4.4 硬件

接著返回 5.2.4.1 postComposition的最后,

        if (mPrimaryDispSync.addPresentFence(presentFence)) {
            ALOGD("in setPostCompositionTime will enableHardwareVsync");
            enableHardwareVsync();
        } else {
            disableHardwareVsync(false);
        }    

如果 addPresentFence見5.2.4.2 返回true, 那么就說明SW vsync和硬件Vsync的誤差已經無法接受了,那么這時就得重新打開硬件Vsync,來重新調節SW vsync模型了。

六、總結

寫文章太累了...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容