「Android研習社」深刻研究源碼:DispSyncy詳解

All about DispSync


前言html

本次投稿活動正在進行中,獎品會由機械工業出版社華章公司統一發出,感謝郭楚謀同窗的投稿和支持,投稿活動還在繼續,能夠關注下android

福利活動算法

Android研習社機械工業出版社華章公司聯合發起的贈書活動正在進行中,歡迎你們點擊連接參與數組

正文

DispSync 是什麼?

在 Android 4.1 的時候,Google 提出了著名的 "Project Butter",引入了 VSYNC,把 app 畫圖,SurfaceFlinger 合成的時間點都規範了起來,減小了掉幀,加強了渲染的流暢度。可是這裏有個問題,由於 VSYNC 是由硬件產生的,一旦產生了你就必須開始幹活,不靈活。假設有這麼一種需求,我但願在 VSYNC 偏移一段時間之後再幹活,那麼這個是硬件 VSYNC 提供不了,因此這個時候就必須引入軟件模型。而 DispSync 就是爲了解決這個需求引入的軟件模型。DispSync 相似於一個 PLL(phase lock loop,鎖相迴路),它經過接收硬件 VSYNC,而後給其餘關心硬件 VSYNC 的組件(SurfaceFlinger 和須要渲染的 app)在指定的偏移之後發送軟件 VSYNC,**而且當偏差在可接受的範圍內,將會關閉硬件 VSYNC。**谷歌的[這篇文檔][1]裏面詳細有一張很是準確的圖:bash

![此處輸入圖片的描述][2]app

(爲了方便,後面全部的硬件 VSYNC 使用 HW-VSYNC 代指,軟件 VSYNC 使用 SW-VSYNC 代指)ide

綜述

前面提到 DispSync 是一個模擬 HW-VSYNC 的軟件模型,在這個模型裏面包含幾個部分:函數

  • DispSync DispSync 的主體,主要負責啓動 DispSyncThread,接收 HW-VSYNC 而且更新計算出 SW-VSYNC 間隔—— mPeriod
  • DispSyncThread DispSync 的一個內部線程類,主要功能是模擬 HW-VSYNC 的行爲,大部分時間都處於阻塞狀態,利用 DispSync 算出的 mPeriod,週期性地在下一個 SW-VSYNC 時間點(加了偏移的)醒來去通知對 VSYNC 感興趣的 Listener —— DispSyncSource
  • DispSyncSource SurfaceFlinger 的一個內部類,實現了 DispSync::Callback 的接口,DispSyncThread 和 EventThread 的中間人
  • EventThread VSYNC 的接收實體,收到 DispSync 的 SF-VSYNC 再進行分發,SurfaceFlinger 和 app 分別有本身的 EventThread—— sfEventThread 和 appEventThread
  • Connection EventThread 內部類,任何一個對 VSYNC 感興趣的(SurfaceFlinger,須要渲染畫面的 app)都會在 EventThread 裏面抽象爲一個 Connection
  • EventControlThread 大部分博客都將其描述爲硬件 VSYNC 的「閘刀」,也就是負責控制硬件 VSYNC 的開關
  • MessageQueue SurfaceFlinger 用來在 sfEventThread 註冊
  • DisplayEventReceiver app 用來在 appEventThread 註冊

下面來詳細描述一下整個初始化的流程。oop

初始化

首先說明一下,DispSync 的初始化流程初看是十分複雜的,首先它涉及到比較多的線程,而且線程在不少時候是處於阻塞狀態的,致使整個流程處於一個不連續的狀態。所以誰把哪一個線程喚醒了就變得十分重要,這也是理解整個初始化過程當中的一個難點。post

DispSync 和 DispSyncThread-01

DispSync 在 SurfaceFlinger 裏只有一個實例 —— mPrimaryDipsSync,它在 SurfaceFlinger 的初始化分兩部分,建立實例 mPrimaryDispSync 而後執行其 init() 方法。DispSync 的構造函數很是簡單,都是一些賦值:

DispSync::DispSync(const char* name)
      : mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {}
      
explicit DispSyncThread(const char* name)
      : mName(name),
        mStop(false),
        mPeriod(0),
        mPhase(0),
        mReferenceTime(0),
        mWakeupLatency(0),
        mFrameNumber(0) {}
複製代碼

而後來看 init() 方法:

void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
    mIgnorePresentFences = !hasSyncFramework;
    mPresentTimeOffset = dispSyncPresentTimeOffset;
    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);

    // set DispSync to SCHED_FIFO to minimize jitter
    struct sched_param param = {0};
    param.sched_priority = 2;
    if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
    }

    reset();
    beginResync();
    ...
}
複製代碼

DispSycn::init() 最主要的就是工做就是讓 DispSyncThread 運行起來,而且將其調度優先級改成 SCHED_FIFO,這樣作的目的是什麼呢?咱們前面提到,DispSyncThread 大部分時間都在阻塞,它會「睡」到下次 SW-VSYNC 開始的時間戳,所以當其被喚醒的時候,高優先級可以保證其儘快地被調度,減小偏差。執行完 mThread->run() 之後,就會開始執行 DispSyncThread::threadLoop()

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 (mStop) {
                return false;
            }

            if (mPeriod == 0) {
                err = mCond.wait(mMutex);
                // 第一次初始化,因爲 mPeriod 爲 0,因此會先 block 在這裏
複製代碼

目前 DispSyncThread 會阻塞在這裏,咱們接下去看。

EventThread-01

在 SurfaceFlinger 初始化的時候,會建立兩個 EventThread,一個給 SurfaceFlinger,一個給 app:

void SurfaceFlinger::init() {
    ...
    // start the EventThread
    mEventThreadSource =
            std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
                                             true, "app");
    mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
                                                       [this]() { resyncWithRateLimit(); },
                                                       impl::EventThread::InterceptVSyncsCallback(),
                                                       "appEventThread");
    mSfEventThreadSource =
            std::make_unique<DispSyncSource>(&mPrimaryDispSync,
                                             SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
    
    mSFEventThread =
            std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
                                                [this]() { resyncWithRateLimit(); },
                                                [this](nsecs_t timestamp) {
                                                    mInterceptor->saveVSyncEvent(timestamp);
                                                },
                                                "sfEventThread");
複製代碼

前面提到,DispSyncSource 是 DispSyncThread 和 EventThread 的中間人,先來看一下 DispSyncSource 的構造函數:

class DispSyncSource final : public VSyncSource, private DispSync::Callback {
public:
    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
        const char* name) :
            mName(name),
            mValue(0),
            mTraceVsync(traceVsync),
            mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
            mVsyncEventLabel(String8::format("VSYNC-%s", name)),
            mDispSync(dispSync),
            mCallbackMutex(),
            mVsyncMutex(),
            mPhaseOffset(phaseOffset),
            mEnabled(false) {}
複製代碼

請注意這裏有一個很是重要的點,就是 mVsyncEventLabel(String8::format("VSYNC-%s", name))。SurfaceFlinger 的 DispSyncSource 傳進來的 name 是 "sf",app 的 DispSyncSource 傳進來的 name 是 "app",因此連起來就是 "VSYNC-sf" 和 "VSYNC-app"。爲何說重要呢?來看一段 systrace:

![Screenshot from 2019-06-26 21-20-38.png-71.5kB][3]

這裏面的 VSYNC-app 和 VSYNC-sf 就是說的 DispSyncSource,至於它的意義,後面會提到。

而後 DispSyncSource 做爲參數傳給 EventThread 的構造函數:

EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
      : mVSyncSource(src),
        mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
        mInterceptVSyncsCallback(interceptVSyncsCallback) {
    for (auto& event : mVSyncEvent) {
        event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
        event.header.id = 0;
        event.header.timestamp = 0;
        event.vsync.count = 0;
    }

    mThread = std::thread(&EventThread::threadMain, this);

    pthread_setname_np(mThread.native_handle(), threadName);

    pid_t tid = pthread_gettid_np(mThread.native_handle());

    // Use SCHED_FIFO to minimize jitter
    constexpr int EVENT_THREAD_PRIORITY = 2;
    struct sched_param param = {0};
    param.sched_priority = EVENT_THREAD_PRIORITY;
    if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO for EventThread");
    }

    set_sched_policy(tid, SP_FOREGROUND);
}
複製代碼

構造函數的最主要功能就是把 EventThread 的線程主體 threadMain 運行起來而且設置其優先級爲 SCHED_FIFO,接下來看 threadMain:

void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
    std::unique_lock<std::mutex> lock(mMutex);
    while (mKeepRunning) {
        DisplayEventReceiver::Event event;
        Vector<sp<EventThread::Connection> > signalConnections;
        signalConnections = waitForEventLocked(&lock, &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);
            ...
    }
}
複製代碼

threadMain 的主要工做是調用 waitForEventLocked 等待一個 Event,而後在一個個地通知 signalConnections。至於這個 EventsignalConnections 分別是什麼,後面會具體描述。如今先來看一下 waitForEventLocked 的邏輯:

// This will return when (1) a vsync event has been received, and (2) there was
// at least one connection interested in receiving it when we started waiting.
Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked(
        std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* event) {
    Vector<sp<EventThread::Connection> > signalConnections;

    while (signalConnections.isEmpty() && mKeepRunning) {
        bool eventPending = false;
        bool waitForVSync = false;

        size_t vsyncCount = 0;
        nsecs_t timestamp = 0;
        // 在前面 EventThread 的構造函數裏面已經把 mVSyncEvent 數組內的全部 timestamp 都置爲 0
        // 所以在第一次初始化的時候,這個循環會直接退出
        for (int32_t i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; i++) {
            ...
        }

        // 第一次初始化的時候 mDisplayEventConnections 的數組也爲空,count 爲 0
        size_t count = mDisplayEventConnections.size();
        if (!timestamp && count) {
            ...
        }

        // 第一次初始化不執行這個循環
        for (size_t i = 0; i < count;) {
            ...
        }

        // timestamp 爲 0, waitForVSync 爲 false
        if (timestamp && !waitForVSync) {
            ...
        } else if (!timestamp && waitForVSync) {
            ...
        }

        // eventPending 爲 false,符合條件
        if (!timestamp && !eventPending) {
            if (waitForVSync) {
                ...
            // waitForVSync 爲 false,進入 else
            } else {
                // 最終,在第一次初始化的時候,EventThread 就阻塞在這裏了
                mCondition.wait(*lock);
            }
        }
    }
    
    ...
}
複製代碼

好,到這裏,SurfaceFlinger 建立的兩個 EventThread 都會阻塞在上面代碼提到的地方,SurfaceFlinger 的初始化繼續執行。

補充:SurfaceFlinger 的啓動

首先說明一下 mEventQueue 是在哪裏被初始化的。是在 SurfaceFlinger 的另外一個方法:

提到這裏就須要 SurfaceFlinger 是怎麼啓動和初始化的。SurfaceFlinger 做爲系統最基本最核心的服務之一,是經過 init.rc 的方式進行啓動的(內容在 frameworks/native/services/surfaceflinger/surfaceflinger.rc):

service surfaceflinger /system/bin/surfaceflinger
    class core animation
    user system
    group graphics drmrpc readproc input
    onrestart restart zygote
    ...
複製代碼

而後就須要提到 SurfaceFlinger 的組成部分,init.rc 裏面提到的 /system/bin/surfaceflinger 這個二進制文件,由 main_surfaceflinger.cpp 這個文件編譯獲得;而上面提到 DispSync,EventThread 等,都被編譯到了 libsurfaceflinger.so 這個庫。這也給了咱們一個啓示:當咱們在本身調試 SurfaceFlinger 的時候,大部分時間都只須要從新編譯 libsurfaceflinger.so 這個文件便可

回來簡單看一下 SurfaceFlinger 是如何啓動的,來看看 main_surfaceflinger.cpp

int main(int, char **) {
    ...
    sp<SurfaceFlinger> flinger = DisplayUtils::getInstance()->getSFInstance();
    ...
    flinger->init();
    ...
複製代碼

這裏的重點就是這個 sp<SurfaceFlinger>,當被 sp 指針引用的時候,會觸發 onFirstRef() 函數:

void SurfaceFlinger::onFirstRef()
{
    mEventQueue->init(this);
}
複製代碼

這樣,就走到了 MessageQueue 部分了:

MessageQueue

接着 EventThread,而後就執行到這裏:

void SurfaceFlinger::init() {
    ...
    mEventQueue->setEventThread(mSFEventThread.get());
複製代碼

mEventQueue 在前面的 SurfaceFlinger::onFirstRef() 中完成了初始化:

void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
    mFlinger = flinger;
    mLooper = new Looper(true);
    mHandler = new Handler(*this);
}
複製代碼

接着來看一下很重要的 setEventThread()

void MessageQueue::setEventThread(android::EventThread* eventThread) {
    if (mEventThread == eventThread) {
        return;
    }

    if (mEventTube.getFd() >= 0) {
        mLooper->removeFd(mEventTube.getFd());
    }

    mEventThread = eventThread;
    mEvents = eventThread->createEventConnection();
    mEvents->stealReceiveChannel(&mEventTube);
    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
                   this);
}
複製代碼

重點來了,前面建立的 SurfaceFlinger 的 EventThread 被做爲參數傳給了 setEventThread,而且執行了 EventThread 的 createEventConnection()。(注意,須要時時刻刻地記住,如今處理的 SurfaceFlinger 的 EventThread

(後面爲了方便,將使用 sfEventThread 指代 SurfaceFlinger 的 EventThread;使用 appEventThread 指代 app 的 EventThread)

EventThread::Connection

sp<BnDisplayEventConnection> EventThread::createEventConnection() const {
    return new Connection(const_cast<EventThread*>(this));
}
複製代碼

在這裏,sfEventThread 迎來了第一個(同時也是惟一的) Connection:

EventThread::Connection::Connection(EventThread* eventThread)
      : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {}

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) { std::lock_guard<std::mutex> lock(mMutex); mDisplayEventConnections.add(connection); mCondition.notify_all(); return NO_ERROR; } 複製代碼

MessageQueue 調用 sfEventThread 的 createEventConnection 建立一個 Connection。因爲 sp 指針的做用,將會調用 Connection::onFirstRef,最終這個 Connection 會被添加到 mDisplayEventConnections 而且喚醒在 EventThread - 01 中阻塞的線程。

EventThread-02

在前面把 EventThread 喚醒後,因爲 signalConnections 爲空,繼續循環。而後因爲新加入的 Connection count 爲 -1,因此這個 EventThread 會繼續阻塞,不過此時 mDisplayEventConnections 裏面已經有一個 Connection 了。接着看下去。

EventControlThread-01

SurfaceFlinger::init() 接着運行到這裏:

void SurfaceFlinger::init() {
    ...
    mEventControlThread = std::make_unique<impl::EventControlThread>(
            [this](bool enabled) { setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled); });
複製代碼

主要提一下的是,這個傳進來的參數是一個 Lambda 表達式,具體的語法不講。稍微解釋一下這裏傳進來的 Lambda 表達式的意義就是,捕獲列表爲 SurfaceFlinger 自己,接受一個布爾參數,當這個 Lamda 表達式被調用的時候,會調用 SurfaceFlinger::setVsyncEnabled() 這個函數,這個函數後面會提到,也是一個很重要的函數。

EventControlThread 的構造函數的主要內容也是啓動一個線程:

EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
      : mSetVSyncEnabled(function) {
    pthread_setname_np(mThread.native_handle(), "EventControlThread");

    pid_t tid = pthread_gettid_np(mThread.native_handle());
    setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
    set_sched_policy(tid, SP_FOREGROUND);
}

void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
    auto keepRunning = true;
    auto currentVsyncEnabled = false;

    while (keepRunning) {
        mSetVSyncEnabled(currentVsyncEnabled);

        std::unique_lock<std::mutex> lock(mMutex);
        // keepRunning 爲 true,currentVsyncEnabled 爲 false,mVsyncEnabled 默認值爲 false,mKeepRunning 默認值爲 true,所以 Lambda 表達式爲 false,線程阻塞
        mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
            return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
        });
        currentVsyncEnabled = mVsyncEnabled;
        keepRunning = mKeepRunning;
    }
}
複製代碼

此時,EventControlThread 也會陷入阻塞之中。而 SurfaceFlinger 也將迎來初始化中最爲複雜的一步。

喚醒全部線程

至此,SurfaceFlinger 總共起了四個線程 —— DispSyncThread,兩個 EvenThread 和 EventControlThread,而且這四個線程全都處於阻塞狀態。致使這些線程處於阻塞狀態的緣由是:

  • DispSyncThread: mPeriod 爲 0
  • EventThread: Connection->count 爲 -1
  • EventControlThread: mVsyncEnabled 爲 false

而後讓咱們一個個將其喚醒。

EventThread-03

接下來的 SurfaceFlinger 會進行很是複雜的初始化操做,EventThread 喚醒相關的調用流程以下(這裏借用了這位大佬[《Android SurfaceFlinger SW Vsync模型》][4]的內容,寫得很是棒,在學習的過程當中可以獲得了很大的啓發):

initializeDisplays();
    flinger->onInitializeDisplays();
        setTransactionState(state, displays, 0);
            setTransactionFlags(transactionFlags);
                signalTransaction();
                    mEventQueue->invalidate();
                        mEvents->requestNextVsync()  //mEvents是Connection實例
                            EventThread->requestNextVsync(this);
複製代碼
void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
    ...
    if (connection->count < 0) {
        connection->count = 0;
        mCondition.notify_all();
    }
}
複製代碼

在這裏把前面建立的那個 Connection 的 count 置爲 0,而且喚醒阻塞的 EventThread,這個時候,mDisplayEventConnections 不爲空而且 count 不爲 -1,能夠正常地運行了,EventThread::waitForEventLocked() 走到了這裏:

} 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.)
            enableVSyncLocked();
        }
        
void EventThread::enableVSyncLocked() {
    // 通常都爲 false
    if (!mUseSoftwareVSync) {
        // never enable h/w VSYNC when screen is off
        if (!mVsyncEnabled) {
            mVsyncEnabled = true;
            mVSyncSource->setCallback(this);
            mVSyncSource->setVSyncEnabled(true);
        }
    }
    mDebugVsyncEnabled = true;
}
複製代碼

調用了 DispSyncSource::setCallback()將 EventThread 和 DispSyncSource 聯繫在了一塊兒

void setCallback(VSyncSource::Callback* callback) override{
        Mutex::Autolock lock(mCallbackMutex);
        mCallback = callback;
    }
複製代碼

接着調用 DispSyncSource::setVSyncEnabled

void setVSyncEnabled(bool enable) override {
        Mutex::Autolock lock(mVsyncMutex);
        // true
        if (enable) {
            status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                    static_cast<DispSync::Callback*>(this));
            ...
    }
複製代碼

最終調用了 DispSync::addEventListener

status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) {
        if (kTraceDetailedInfo) ATRACE_CALL();
        Mutex::Autolock lock(mMutex);

        // 保證了 mEventListeners 的惟一性
        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 = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency;

        mEventListeners.push(listener);

        // 喚醒 DispSyncThread
        mCond.signal();

        return NO_ERROR;
    }
複製代碼

把 DispSyncSource 加到 mEventListeners,將 DispSync 和 DispSyncSource 聯繫在了一塊兒,而且把前面阻塞的 DispSyncThread 喚醒,可是因爲 mPeriod 仍是爲 0,所以 DispSyncThread 仍是會繼續阻塞。

不過此時從調用關係已經初步能夠看到前面我說的那句 DispSyncSource 是 DispSync 和 EventThread 的中間人 是正確的了。

接着來看 DispSyncThread。

DispSync 和 DispSyncThread-02

設置 mPeriod 的流程以下(依舊引用了這位大佬的[《Android SurfaceFlinger SW Vsync模型》][4]的內容,再次感謝):

initializeDisplays();
    flinger->onInitializeDisplays();
        setPowerModeInternal()
            resyncToHardwareVsync(true);
                repaintEverything();
複製代碼

這裏把 SurfaceFlinger::resyncToHardwareVsync() 分爲兩部分,先看上部分:

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

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

    const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
    const nsecs_t period = activeConfig->getVsyncPeriod();

    mPrimaryDispSync.reset();
    // 設置 mPeriod
    mPrimaryDispSync.setPeriod(period);

    // 默認爲 false
    if (!mPrimaryHWVsyncEnabled) {
        mPrimaryDispSync.beginResync();
        // 上部分結束
複製代碼

DispSync::setPeriod() 裏面給 mPeriod 賦值,而且把 DispSyncThread 喚醒:

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

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;
    ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
          " mReferenceTime = %" PRId64,
          mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime));
    // 這裏把 DispSyncThread 喚醒
    mCond.signal();
}
複製代碼

至此,DispSyncThread 也開始運轉。

EventControlThread-02

接着看 SurfaceFlinger::resyncToHardwareVsync() 的下半部分:

...
        mEventControlThread->setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}

void EventControlThread::setVsyncEnabled(bool enabled) {
    std::lock_guard<std::mutex> lock(mMutex);
    mVsyncEnabled = enabled;
    // 把 EventControlThread 喚醒
    mCondition.notify_all();
}
複製代碼

把 EventControlThread 喚醒之後,會從新把 SurfaceFlinger 傳進來的那個被 Lambda 表達式包裹的 SurfaceFlinger::setVsyncEnabled() 從新執行一下:

void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) {
    ATRACE_CALL();
    Mutex::Autolock lock(mStateLock);
    getHwComposer().setVsyncEnabled(disp,
            enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
}

void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) {
    if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) {
        ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId);
        return;
    }

    RETURN_IF_INVALID_DISPLAY(displayId);

    // NOTE: we use our own internal lock here because we have to call
    // into the HWC with the lock held, and we want to make sure
    // that even if HWC blocks (which it shouldn't), it won't
    // affect other threads.
    Mutex::Autolock _l(mVsyncLock);
    auto& displayData = mDisplayData[displayId];
    if (enabled != displayData.vsyncEnabled) {
        ATRACE_CALL();
        auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
        RETURN_IF_HWC_ERROR(error, displayId);

        displayData.vsyncEnabled = enabled;

        char tag[16];
        snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
        // 在 systrace 看到的就是在這裏
        ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
    }
}
複製代碼

在這裏,真正地去開啓 HW-VSync。而後因爲 SurfaceFlinger 接收了 HW-VSync,而後展轉發給 DispSync,DispSync 接收,校訂 SW-VSYNC。而整個 DispSync SurfaceFlinger 部分的初始化的流程也最終完成。

注意,上面說的是 SurfaceFlinger 部分。前面提到,總共有兩個 EventThread,而上面分析的都是 sfEventThread,下面簡單地描述一下 appEventThread 的流程,其實 EventThread 到 DispSync 這部分都是一致的,只是 EventThread 的 Connection 的註冊流程不同。sfEventThread 是 MessageQueue 去註冊 Connection,而 appEventThread 則是另外一種方法。

appEventThread

SurfaceFlinger 接收 VSYNC 是爲了合成,所以 sfEventThread 的 Connection 只有一個,就是 SurfaceFlinger 自己;而 app 接收 VSYNC 是爲了畫幀,appEventThread 會有不少不少個 Connection。

app 自己是如何在 appEventThread 註冊一個 Connection 的,與這篇文章的主體有點偏移,這個能夠另開一篇文章來詳細說明,流程也是很是複雜,這裏只簡單地描述:核心就是 libgui 下面的 DisplayEventReceiver,它在初始化的時候會調用 SurfaceFlinger::createEventConnection

sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
        ISurfaceComposer::VsyncSource vsyncSource) {
    if (vsyncSource == eVsyncSourceSurfaceFlinger) {
        return mSFEventThread->createEventConnection();
    } else {
        return mEventThread->createEventConnection();
    }
}
複製代碼

而後後面的流程就跟前面的一致了。

小結

經過上面的描述,依據各個類的依賴關係,其實能夠總結出這麼一個圖:

![][7]

請注意箭頭方向。

運做流程

前面提到,引入 DispSync 的目的是爲了經過 SF-VSYNC 來模擬 HW-VSYNC 的行爲而且經過加入 offset 來讓通知時機變得靈活。所以理解整個 DispSync 的流程就能夠歸結爲下面幾個部分:SF-VSYNC 通知週期 mPeriod 的計算;SF-VSYNC 的模擬方式以及 SF-VSYNC 傳遞流程,分別來看。

mPeriod 計算邏輯

前面提到,DispSync 經過接收 HW-VSYNC 而且更新計算出 SW-VSYNC 間隔—— mPeriod,首先看一下 DispSync 是如何收到 HW-VSYNC。

先看一下 SurfaceFlinger 這個類:

class SurfaceFlinger : public BnSurfaceComposer,
                       public PriorityDumper,
                       private IBinder::DeathRecipient,
                       private HWC2::ComposerCallback
複製代碼

SurfaceFlinger 實現了 HW2::ComposerCallback 的接口,而後當 HW-VSYNC 到來的時候,HWC 會將 HW-VSYNC 發生的時間戳發給 SurfaceFlinger,而後 SurfaceFlinger 會轉發給 DispSync:

class ComposerCallbackBridge : public Hwc2::IComposerCallback {
public:
    ...
    Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
    {
        mCallback->onVsyncReceived(mSequenceId, display, timestamp);
        return Void();
    }
    ...
};

void SurfaceFlinger::onVsyncReceived(int32_t sequenceId,
        hwc2_display_t displayId, int64_t timestamp) {
    ...
    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
        }
    }

    // 這個很重要,後面會提到
    if (needsHwVsync) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}
複製代碼

重點看 DispSync 怎麼處理這些 HW-VSYNC,是在 addResyncSample() 這個函數:

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

    size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
    mResyncSamples[idx] = timestamp;
    if (mNumResyncSamples == 0) {
        mPhase = 0;
        mReferenceTime = timestamp;
        mThread->updateModel(mPeriod, mPhase, mReferenceTime);
    }

    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
        mNumResyncSamples++;
    } else {
        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
    }

    updateModelLocked();

    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
        resetErrorLocked();
    }

    ...
    
    bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
    return !modelLocked;
}
複製代碼

這裏須要重點說明這裏面幾個變量的意義(在 DispSync.h 這個頭文件裏面有說明):

  • mPeriod 這個就是 DispSync 根據 HW-VSYNC,計算出來的 SW-VSYNC 的時間間隔,單位是納秒。 這裏有人可能會有疑問,這個值的意義在哪?硬件是以一個固定的時間間隔去發 HW-VSYNC,爲何還須要去計算一個新的時間間隔?直接跟 HW-VSYNC 的時間間隔一致不行嗎? 這個當作做業留給你們思考。
  • mPhase 這個說實話我看了很久一直都看不懂這個值的意義
  • mReferenceTime 這個是第一次收到 HW-VSYNC 的時間戳,用來當作 DispSync 的參考標準
  • mWakeupLatency
  • mResyncSample 長度 32,用來記錄收到硬件 VSYNC 的時間戳的數組,不過被解釋爲一個 ring buffer,新的會覆蓋舊的
  • mFirstResyncSample 記錄了 mResyncSample 這個 ring buffer 的開頭
  • mNumResyncSamples 接收到硬件 VSYNC 的個數

DispSync 將從 SurfaceFlinger 發來的 HW-VSYNC 的時間戳都給記錄到一個 ring buffer,當有了足夠多的 HW-VSYNC 了之後(目前是 6 個即以上),就能夠開始來擬合 SF-VSYNC 的間隔 mPeriod 了,是在 DispSync::updateModelLocked() 裏面計算的,核心算法就在這裏了。分爲兩部分,一部分是 mPeriod 的計算:

void DispSync::updateModelLocked() {
    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
        nsecs_t durationSum = 0;
        nsecs_t minDuration = INT64_MAX;
        nsecs_t maxDuration = 0;
        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;
            nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
            durationSum += duration;
            minDuration = min(minDuration, duration);
            maxDuration = max(maxDuration, duration);
        }

        durationSum -= minDuration + maxDuration;
        mPeriod = durationSum / (mNumResyncSamples - 3);
        ...
複製代碼

mPeriod 的計算十分簡單,把全部的 HW-VSYNC 先後相減算出 HW-VSYNC 的時間間隔,而後去掉一個最小值和最大值,而後全部 HW-VSYNC 的時間戳之和除以總個數就是 mPeriod 了。這裏有一個問題就是爲何在最後除的時候是除數是 3?其實很簡單,由於前面的 for 循環是從 1 開始算起的,因此循環結束一下 durationSum 實際上是 mNumResyncSamples - 1 個 HW-VSYNC 的總和,而後再去掉一個最大和最小,因此總數是 mNumResyncSamples - 3。

另外一部分是 mPhase 的計算,這一塊看上去好像挺複雜的,甚至還有三角函數:

...
    double sampleAvgX = 0;
    double sampleAvgY = 0;
    // scale 的意義是,每 ms 表明了多少度。(總量除以總個數等於每一個的值)
    double scale = 2.0 * M_PI / double(mPeriod);
    // Intentionally skip the fist sample
    for (size_t i = 1; i < mNumResyncSamples; i++) {
        size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
        // sample 是偏差
        nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
        // 這裏 (sample % mPeriod) 看上去挺唬人的,可是其實就是保證 sample 不會大於或者等於 mPeriod,不然這裏的 samplePhase 算出來就是 2π 了
        // 因此這裏 samplePhase 算出來的就是把偏差轉成度數
        double samplePhase = double(sample % mPeriod) * scale;
        // 這兩個後面是爲了用來計算偏差平均的度數
        sampleAvgX += cos(samplePhase);
        sampleAvgY += sin(samplePhase);
    }
    
    sampleAvgX /= double(mNumResyncSamples - 1);
    sampleAvgY /= double(mNumResyncSamples - 1);

    // 根據等比關係,算出平局偏差度數對應的 ns 值
    mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);

    ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase));

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

    // Artificially inflate the period if requested.
    mPeriod += mPeriod * mRefreshSkipCount;

    mThread->updateModel(mPeriod, mPhase, mReferenceTime);
    mModelUpdated = true;
複製代碼

上面的邏輯其實能夠用下圖來闡述:

而 mPhase 最終是根據下面的等比公式計算出來的:

\frac{2\pi}{mPeriod} = \frac{Angle}{mPhase}

最後,看一下 DispSync::addResyncSample 這個函數的返回值,這個返回值很是重要,當經過統計 SW-VSYNC 的偏差小於閾值的時候(這個偏差的計算涉及到了 Fence,目前我對這部份內容理解得還不是很透徹,等完全理解了之後再來填坑),返回 true 給 SurfaceFlinger 的時候,SurfaceFlinger 則會調用 SurfaceFlinger::disableHardwareVsync 把 HW-VSYNC 給關了。

SW-VSYNC 的生成與傳遞

mPeriod 計算出來之後,DispSyncThread 就能夠依據這個值來模擬 HW-VSYNC 了(實際上計算流程和模擬流程是相互獨立的,分別在兩個不一樣的線程上完成),因此流程都在 DispSyncThread 的 threadLoop() 裏面:

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 (mStop) {
                    return false;
                }

                if (mPeriod == 0) {
                    err = mCond.wait(mMutex);
                    if (err != NO_ERROR) {
                        ALOGE("error waiting for new events: %s (%d)", strerror(-err), err);
                        return false;
                    }
                    continue;
                }

                // 這裏計算出下一個確切的 SW-VSYNC 的時間戳
                targetTime = computeNextEventTimeLocked(now);

                bool isWakeup = false;

                if (now < targetTime) {
                    if (targetTime == INT64_MAX) {
                        err = mCond.wait(mMutex);
                    } else {
                        // 睡到下一個 SW-VSYNC 爲止
                        err = mCond.waitRelative(mMutex, targetTime - now);
                    }

                    if (err == TIMED_OUT) {
                        isWakeup = true;
                    } else if (err != NO_ERROR) {
                        return false;
                    }
                }

                now = systemTime(SYSTEM_TIME_MONOTONIC);

                // Don't correct by more than 1.5 ms static const nsecs_t kMaxWakeupLatency = us2ns(1500); if (isWakeup) { mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64; mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency); } callbackInvocations = gatherCallbackInvocationsLocked(now); } if (callbackInvocations.size() > 0) { fireCallbackInvocations(callbackInvocations); } } return false; } 複製代碼

這裏經過依據 mPeriod 算出下一個 SW-VSYNC 的時間戳,計算 SW-VSYNC 的時間戳的邏輯比較簡單,就不過多描述。而後經過條件變量直接睡到下一個 SW-VSYNC,而後一個個地經過調用 DispSyncSource 的 onDispSyncEvent 回調來進行 SW-VSYNC 的通知。而後 DispSyncSource 的 onDispSyncEvent 又會調用 EventThread 的 onVSyncEvent:

void EventThread::onVSyncEvent(nsecs_t timestamp) {
    std::lock_guard<std::mutex> lock(mMutex);
    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
    mVSyncEvent[0].header.id = 0;
    mVSyncEvent[0].header.timestamp = timestamp;
    mVSyncEvent[0].vsync.count++;
    mCondition.notify_all();
}
複製代碼

這裏就能夠回答一下在提到的問題,mVSyncEvent 和 mDisplayEventConnections 以及 signalConnections 這三個數組的意義和區別:

  • mVSyncEvent 一個長度爲 NUM_BUILTIN_DISPLAY_TYPES 的數組,表明這一個 Vsync Event,這個多是 VSYNC 事件,也有多是屏幕插拔這種事件等。這個 NUM_BUILTIN_DISPLAY_TYPES 是一個 enum 變量:
enum DisplayType {
       DISPLAY_ID_INVALID = -1,
       DISPLAY_PRIMARY     = HWC_DISPLAY_PRIMARY,
       DISPLAY_EXTERNAL    = HWC_DISPLAY_EXTERNAL,
       DISPLAY_VIRTUAL     = HWC_DISPLAY_VIRTUAL,
       NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
   };
複製代碼

從這裏就能夠看到,至少在這個版本的 Android 除了一個 virtual display(這是 SurfaceFlinger 提供的一個很是有用的功能,不少常見的需求例如錄屏就是經過 virtual display 來實現的,這裏不展開,有須要的話再寫一篇文章詳細描述)已是支持多屏幕了,只不過呢,目前的代碼裏面都是寫死只處理主屏,也就是 Display 0 的事件。

  • mDisplayEventConnections 這個就是用來存儲前面提到的 EventThread::Connection 的數組,在調用 EventThread::registerDisplayEventConnection() 的時候,就會把這個 Connection 加到這個數組裏面。
  • signalConnections EventThread::waitForEventLocked 最大的做用就是返回這個數組,這個數組存的是全部但願接收下一個 SW-VSYNC 的 Connection,而是否接收 Connection 的標誌是 connection->count 的值:-1 表明不接收 SW-VSYNC;0 表明只接收一次,EventThread 發現 connection->count 的值爲 0 的時候,會把它加到 signalConnections 以便其可以接受到這一次的 SW-VSYNC 以後,會將其 count 置爲 -1;大於 0 就代表會一直接收。

onVSyncEvent 的做用是新增一個 VSyncEvent 而且把 EventThread 喚醒,EventThread 統計了全部對 SW-VSYNC 感興趣的 Connection 而且都加到 signalConnections,最後會經過一個循環調用每一個 connection 的 postEvent() 函數,SurfaceFlinger 就會開始走合成的流程,app 就會開始走渲染的流程。至此,SW-VSYNC 完成了傳遞的全過程。

小結

當整個初始化完成之後,整個 DispSync 模型就開始運做起來了。咱們先簡單地把整個流程描述一下:

SurfaceFlinger 經過實現了 HWC2::ComposerCallback 接口,當 HW-VSYNC 到來的時候,SurfaceFlinger 將會收到回調而且發給 DispSync。DispSync 將會把這些 HW-VSYNC 的時間戳記錄下來,當累計了足夠的 HW-VSYNC 之後(目前是大於等於 6 個),就開始計算 SW-VSYNC 的偏移 mPeriod。計算出來的 mPeriod 將會用於 DispSyncThread 用來模擬 HW-VSYNC 的週期性起來而且通知對 VSYNC 感興趣的 Listener,這些 Listener 包括 SurfaceFlinger 和全部須要渲染畫面的 app。這些 Listener 經過 EventThread 以 Connection 的抽象形式註冊到 EventThread。DispSyncThread 與 EventThread 經過 DispSyncSource 做爲中間人進行鏈接。EventThread 在收到 SW-VSYNC 之後將會把通知全部感興趣的 Connection,而後 SurfaceFlinger 開始合成,app 開始畫幀。在收到足夠多的 HW-VSYNC 而且在偏差容許的範圍內,將會關閉經過 EventControlThread 關閉 HW-VSYNC。

而後這個流程咱們能夠獲得下面這張跟初始化很是接近,只是方向相反的 SW-VSYNC 的傳遞圖:

![][8]

爲何要引入偏移

寫了這麼多內容,可能不少人仍是沒法理解引入軟件模型的意義所在,前面咱們提到是讓整個流程更加靈活這句話可能也不是很好理解,所以在這裏詳細描述一下。

首先呢,先來看一下 DispSync 的第一個提交的 commit message,它詳細地描述了引入了 DispSync 的初衷:

commit faf77cce9d9ec0238d6999b3bd0d40c71ff403c5
Author: Jamie Gennis <jgennis@google.com>
Date:   Tue Jul 30 15:10:32 2013 -0700

   SurfaceFlinger: SW-based vsync events
    
    This change adds the DispSync class, which models the hardware vsync event
    times to allow vsync event callbacks to be done at an arbitrary phase offset
    from the hardware vsync.  This can be used to reduce the minimum latency from
    Choreographer wake-up to on-screen image presentation.
    
    Bug: 10624956
    Change-Id: I8c7a54ceacaa4d709726ed97b0dcae4093a7bdcf
複製代碼

意思就是但願可以經過 DispSync 來減小 app 渲染的內容到屏幕的事件延遲,也就是傳說中的跟手性。這裏須要說明一下從 app 渲染畫面到顯示到屏幕的一個簡易 pipeline(這部份內容參考了[這篇博客][5],建議細讀,寫得十分好!)。

首先須要說明的是,爲了嚴格保證顯示的流暢,防止畫面撕裂的狀況發生,畫面更新到屏幕面板須要在 HW-VSYNC 開始的時候才作。

沒有 DispSync 的時候:

  1. 第 1 個 HW-VSYNC 到來時, App 正在畫 N, SF 與 Display 都沒 buffer 可用
  2. 第 2 個 HW-VSYNC 到來時, App 正在畫 N+1, SF 組合 N, Display 沒 Buffer 可顯示
  3. 第 3 個 HW-VSYNC 到來時, App 正在畫 N+2, SF 組合 N+1, Display 顯示 N
  4. 第 4 個 HW-VSYNC 到來時, App 正在畫 N, SF 組合 N+2, Display 顯示 N+1

從上面這個簡易的 pipeline 能夠看到,App 畫的幀得得兩個 HW-VSYNC 以後才能顯示到屏幕面板上,也就是大概 33.3ms。可是,如今大部分的狀況是,硬件的性能已經足夠快了,畫一幀的時間和合成的時間不須要一個 HW-VSYNC 了,這個時候 DispSync 的做用就來了。經過引入 offset,當 offset 爲正值時,App 和 SurfaceFlinger 都是在 HW-VSYNC 日後 offset ms 纔開始工做的,這個時候 App 畫幀到最終顯示到面板上的延遲就變成了 (2 * VSYNC_PERIOD - (offset % VSYNC_PERIOD)),這樣就變相地減小了這個延遲,加強了跟手性,其實這個就是當初引入 DispSync 的初衷。

反過來能夠這麼想,假設把 offset 變爲負值,這個時候 App 渲染和 SurfaceFlinger 合成可用的時間就變長了,在某些負載比較重的場景,這個能夠優化渲染性能。

甚至還有這種狀況,假設在某些場景,App 渲染和 SurfaceFlinger 合成的總時間都足夠短,那麼若是設置合理的話,例如 app 的 offset 設置爲 0,SurfaceFlinger 的 offset 設置爲 VSYNC_PERIOD/2,那麼就可以保證 App 渲染到顯示到面板的時間差在一個 HW-VSYNC 內完成。

從上面的分析就能夠看到,這個就是引入軟件模型的靈活性的體現,根據不一樣的需求對 offset 進行不一樣的取值,能夠獲得不一樣的效果。

有什麼用?

學了 DispSync 有什麼用呢?其實不是說學了 DispSync 有用,而是透過 DispSync 咱們學到了 VSYNC 分發的整個流程,這個可以去解釋不少問題。這裏舉一個例子。前段時間一加 7 Pro 推出了首款 90 Hz 屏幕的手機,不少評測機構都紛紛表示,微博滑動等界面感受更加流暢了,這背後的原理是什麼呢?這個時候就可使用前面學到的知識來分析一波了。這裏的 90 Hz 指的就是 HW-VSYNC。而後根據前面的渲染 pipeline,在沒有 DispSync 的狀況下,因爲 HW-VSYNC 的從普通的 60 Hz變成了 90 Hz,VSYNC 的時間間隔從 16.6ms 減小到了 11.1ms,從前面的 pipeline 能夠得出,app 從渲染到顯示的延遲減小了 10ms 左右,這個延遲減小是十分明顯的,所以會有一個「流暢」的感受。所以可否這麼想的,當屏幕的刷新率變成了 90 Hz 甚至是 120 Hz 之後,DispSync 的做用可能就愈來愈小了,那個時候谷歌會不會把它去掉呢?這個能夠看一下後面 Android 的改動,至少在目前,在這個 90 Hz 即將普及的今天,Android Q 的 DispSync 仍是保留着的。

[1]: https://source.android.com/devices/graphics/implement-vsync
  [2]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc9e14b7d50?w=500&h=134&f=png&s=7554
  [3]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc8b37fee0a?w=1920&h=332&f=png&s=73263
  [4]: https://www.jianshu.com/p/d3e4b1805c92
  [5]: http://echuang54.blogspot.com/2015/01/dispsync.html
  [6]: https://source.android.com/devices/graphics/implement-vsync.html#explicit_synchronization
  [7]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc8b17ecf1b?w=952&h=590&f=png&s=50643
  [8]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc8b19009fe?w=1244&h=590&f=png&s=58913
複製代碼

鄭重聲明

本文由Android研習社代發,原做者爲郭楚謀,版權歸原做者全部,未經容許,禁止轉載,侵權必究!

原文連接:tinylab.org/android-dis…

相關文章
相關標籤/搜索