AndroidQ 應用層Vsync信號的註冊與接收(下)

上一篇文章主要分析了java層DisplayEventReceiver中的nativeInit函數,這個函數主要作用就是創建了應用層和surfaceFlinger的連接,通過一對socket,對應mReceiveFd和mSendFd,應用層通過native層Looper將mReceiveFd加入監聽,隨時等待mSendFd的寫入

這篇文章就來分析什麼時候mSendFd會寫入數據,以及如何傳遞到應用層,核心函數nativeScheduleVsync,這個函數用於應用層向native層註冊監聽下一次Vsync信號,在UI需要刷新時調用

nativeScheduleVsync

//android_view_DisplayEventReceiver.cpp
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
 
    sp<NativeDisplayEventReceiver> receiver =
            reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
    status_t status = receiver->scheduleVsync();
    if (status) {
        String8 message;
        message.appendFormat("Failed to schedule next vertical sync pulse.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
    }
}

nativeInit時java層保存了NativeDisplayEventReceiver的指針,這裏將它傳遞到native層,強轉爲NativeDisplayEventReceiver對象,並調用它的scheduleVsync函數

scheduleVsync

NativeDisplayEventReceiver繼承DisplayEventReceiver,scheduleVsync這個函數是在父類實現的

status_t DisplayEventDispatcher::scheduleVsync() {
    if (!mWaitingForVsync) {
        ALOGV("dispatcher %p ~ Scheduling vsync.", this);

        // Drain all pending events.
        nsecs_t vsyncTimestamp;
        PhysicalDisplayId vsyncDisplayId;
        uint32_t vsyncCount;
        //步驟1
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
                    this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
        }
        //步驟2
        status_t status = mReceiver.requestNextVsync();
        if (status) {
            ALOGW("Failed to request next vsync, status=%d", status);
            return status;
        }

        mWaitingForVsync = true;
    }
    return OK;
}

mWaitingForVsync用來避免短時間重複請求,等一個Vsync處理完之後才能處理下一個,這個函數分爲兩個步驟來分析

我們先看步驟1,processPendingEvents這個函數的作用看名字大概是處理正在準備中的事件

processPendingEvents

bool DisplayEventDispatcher::processPendingEvents(
        nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
    bool gotVsync = false;
    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
    ssize_t n;
    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
        ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
        for (ssize_t i = 0; i < n; i++) {
            const DisplayEventReceiver::Event& ev = buf[i];
            switch (ev.header.type) {
            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
                // Later vsync events will just overwrite the info from earlier
                // ones. That's fine, we only care about the most recent.
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                ......
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
               .....
                break;
            default:
                
                break;
            }
        }
    }
    ...
    return gotVsync;
}

這個函數主要作用就是通過mReceiver.getEvents獲取事件,mReceiver類型爲DisplayEventReceiver,然後處理事件,獲取到的事件放入大小爲100的buf中,
static const size_t EVENT_BUFFER_SIZE = 100

然後根據不同類型的事件,調用不同的處理方式,事件有三種,定義在
DisplayEventReceiver.h中

    //DisplayEventReceiver.h
    enum {
        DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
        DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
        DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
    };
    

我們這裏只關注VSYNC事件,來看看如何獲取

mReceiver.getEvents

ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
        size_t count) {
    return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
}

ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel,
        Event* events, size_t count)
{
    return gui::BitTube::recvObjects(dataChannel, events, count);
}

前一篇文章分析過,mDataChannel是在nativeInit過程中創建的,這個過程創建了一套”app“的連接用來接收Vsync信號,而gui::BitTube則作爲這套連接的數據收發的管理類

gui::BitTube::recvObjects

//BitTube.h
template <typename T>
    static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
        return recvObjects(tube, events, count, sizeof(T));
    }

三個參數recvObjects調用內部四個參數的recvObjects

ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {

    char* vaddr = reinterpret_cast<char*>(events);

    ssize_t size = tube->read(vaddr, count * objSize);
    .....
    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}

先來說一下recvObjects的參數,tube不用說,events指向一個DisplayEventReceiver::Event的buff數組,count爲buff的長度100,objSize爲每個DisplayEventReceiver::Event的大小

recvObjects函數中最重要的就是tube->read這個函數,來看看

tube->read

ssize_t BitTube::read(void* vaddr, size_t size) {
    ssize_t err, len;
    do {
        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    if (err == EAGAIN || err == EWOULDBLOCK) {
        return 0;
    }
    return err == 0 ? len : -err;
}

我們看到read這個函數就是調用recv函數來接收數據,到這裏我們來簡單說說socket中send和recv函數:

socket在內核中有一個發送緩衝區和一個接收緩衝區,send函數負責將數據存入socket的發送緩衝區,recv函數負責從socket的接收緩衝區中將數據拷貝到用戶空間,recv和send不一定是一一對應,也就是說並不是send一次,就一定recv一次接收完,有可能send一次,recv多次才能接收完,也可能send多次,一次recv就接收完了

簡單理解就是send發數據,recv就能夠收到數據,再來看看recv函數的參數是什麼意思:

int recv(mReceiveFd, vaddr, size, MSG_DONTWAIT)

mReceiveFd是接收數據的socket描述符,mReceiveFd這個Fd在上一篇文章分析過,它會接收來自mSendFd的數據

vaddr指明一個緩衝區,該緩衝區用來存放recv函數接收到的數據

size指明緩衝區的大小

MSG_DONTWAIT是一個flag,爲0

recv函數的意思就是將mReceiveFd接收到的數據,放入大小爲size的vaddr緩衝區,最後返回其實際接收的字節數,如果recv在接收數據時出錯,那麼它返回SOCKET_ERROR(-1),如果recv在等待接收數據時網絡中斷了,那麼它返回0。

到這裏我們回到DisplayEventDispatcher的processPendingEvents函數:

bool DisplayEventDispatcher::processPendingEvents(
        nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
    bool gotVsync = false;
    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
    ssize_t n;
    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
        for (ssize_t i = 0; i < n; i++) {
            const DisplayEventReceiver::Event& ev = buf[i];
            switch (ev.header.type) {
            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
                ....
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                ...
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
                ...
                break;
            default:
                ...
                break;
            }
        }
    }
    if (n < 0) {
        ...
    }
    return gotVsync;
}

這個函數其實邏輯挺簡單的,調用mReceiver.getEvents讀取mReceiveFd的數據,放入buf中,buf的長度爲100,然後遍歷此數組,拿出所有的Event,對應不同類型,做不同處理

我們再來看看DisplayEventReceiver::Event這個結構體

struct Event {

        struct Header {
            uint32_t type;
            PhysicalDisplayId displayId;
            nsecs_t timestamp __attribute__((aligned(8)));
        };

        struct VSync {
            uint32_t count;
        };

        struct Hotplug {
            bool connected;
        };

        struct Config {
            int32_t configId;
        };

        Header header;
        union {
            VSync vsync;
            Hotplug hotplug;
            Config config;
        };
    };

這個Event結構體包含三種類型的Event,用一個union來描述,每種類型的Event都有它們特有的變量,VSync有count,Hotplug有connected,Config有configId,另外還有一個公有的結構體Header,Header中包含一個Event的類型,一個DisplayId,一個Event的時間戳

我們只關注Event的類型是Vsync的情況,走如下處理分支:

case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
                ....
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break;

這個分支其實含義就是將Vsync的timestamp,displayId,count全部保存下來,因爲是循環遍歷,所以如果buf中的Vsync非常多,那麼只會保存最新的的那個Vsync的信息

到此processPendingEvents這個函數已經分析完了,它的作用是獲取最新到來的Vsync信號,並保存下它的timestamp,displayId,count,並返回gotVsync這個bool值代表是否獲取到了Vsync

再把scheduleVsync函數貼出來看看

status_t DisplayEventDispatcher::scheduleVsync() {
    if (!mWaitingForVsync) {
        nsecs_t vsyncTimestamp;
        PhysicalDisplayId vsyncDisplayId;
        uint32_t vsyncCount;
        //步驟1
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
                    this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
        }
        //步驟2
        status_t status = mReceiver.requestNextVsync();
        if (status) {
            ALOGW("Failed to request next vsync, status=%d", status);
            return status;
        }
        mWaitingForVsync = true;
    }
    return OK;
}

我們會發現processPendingEvents在scheduleVsync中調用似乎沒什麼實際作用,不管返回true還是false都會繼續請求Vsync

我們繼續看步驟2,這一看就是請求Vsync的操作

mReceiver.requestNextVsync

status_t DisplayEventReceiver::requestNextVsync() {
    if (mEventConnection != nullptr) {
        mEventConnection->requestNextVsync();
        return NO_ERROR;
    }
    return NO_INIT;
}

在前一篇文章分析過,mEventConnection是在nativeInit過程中創建的,類型爲BpDisplayEventConnection,
這裏最終通過Binder調到surfaceFlinger進程中BnDisplayEventConnection的實現類EventThreadConnection中,

//EventThread.cpp
void EventThreadConnection::requestNextVsync() {
    ATRACE_NAME("requestNextVsync");
    mEventThread->requestNextVsync(this);
}

void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
    if (connection->resyncCallback) {
        connection->resyncCallback();
    }

    std::lock_guard<std::mutex> lock(mMutex);

    if (connection->vsyncRequest == VSyncRequest::None) {
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();
    }
}

resyncCallback在前一篇文章分析過,resyncCallback類型爲ResyncCallback,定義在EventThread.cpp中,std::function這是個新API,大致功能是將一段可執行函數封裝到ResyncCallback中,僅從功能上看,類似函數指針

using ResyncCallback = std::function<void()>;

在nativeInit過程中,創建EventThreadConnection時創建的resyncCallback,並將它保存在了EventThreadConnection的resyncCallback成員變量中,resyncCallback中封裝瞭如下這段代碼

ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
    std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
    return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
        if (const auto vsync = ptr.lock()) {
            vsync->resync(getVsyncPeriod);
        }
    };
}

所以connection->resyncCallback不爲空,接着調用resyncCallback()函數,調用resyncCallback時就相當於執行makeResyncCallback return的這段代碼,即vsync->resync

void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
    //將500ms換算成ns
    static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
    //獲取當前時間
    const nsecs_t now = systemTime();
    const nsecs_t last = lastResyncTime.exchange(now);
    LOGI("kIgnoreDelay = :%ld,now = :%ld,last = :%ld",long(kIgnoreDelay),long(now),long(last));

    if (now - last > kIgnoreDelay) {
        scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
    }
}

在分析到這裏的時候我在上面代碼中打了log,發現(now - last > kIgnoreDelay)這個條件幾乎返回的都是false,所以應用層向註冊Vsync肯定不是通過resync後續進行的,我現在還不清楚這個函數的意義,這個函數暫時不分析了

繼續回到requestNextVsync函數,我們跳過resync

void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {

    if (connection->resyncCallback) {
        //不是最終註冊Vsync的代碼,不清楚它的意義,暫時跳過
        connection->resyncCallback();
    }

    std::lock_guard<std::mutex> lock(mMutex);
   
    if (connection->vsyncRequest == VSyncRequest::None) {
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();
    }
}

VSyncRequest定義在EventThread.h中

enum class VSyncRequest {
    None = -1,
    Single = 0,
    Periodic = 1,
    // Subsequent values are periods.
};

代表了當前VSync請求的三種狀態,None爲初始值,Single代表已經請求了一個VSync,是一次性的,Periodic代表請求的是週期性信號

我們收到VSync是需要自己請求的,請求一次,接收一次,而底層的VSync是以週期性在發送的,通常情況爲16.6ms一次,所以這裏定義了這三種VSync請求的狀態

看到這裏就能夠知道爲什麼應用層請求的Vsync是一次性的了
當VSyncRequest爲none時,給它賦值爲Single,接着調用 mCondition.notify_all()函數

mCondition定義在EventThread.h中,類型爲std::condition_variable,這是一個條件變量,它在這裏最大的用處就是喚醒/等待
這裏引用轉 C++11 併發指南std::condition_variable詳解對條件變量的介紹

std::condition_variable 是條件變量
當 std::condition_variable 對象的某個 wait 函數被調用的時候,它使用 std::unique_lock(通過 std::mutex) 來鎖住當前線程。當前線程會一直被阻塞,直到另外一個線程在相同的 std::condition_variable 對象上調用了 notify 函數來喚醒當前線程。
std::condition_variable 對象通常使用 std::unique_lock<std::mutex> 來等待,如果需要使用另外的 lockable 類型,可以使用 std::condition_variable_any 類

我們主要關注這裏條件變量使用notify_all會喚醒所有調用了wait的線程,wait在哪裏調用的呢?是在EventThread創建的時候,它的構造函數中創建了一個線程,這個線程一啓動就調用了threadMain函數,threadMain中通過條件變量調用wait使線程陷入等待

EventThread是在SurfaceFlinger的init中就創建了,有兩種類型,一個名爲”app“,一個名爲”sf“,需要注意把EventThread和EventThreadConnection分開

EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
      : mVSyncSource(src),
        mVSyncSourceUnique(std::move(uniqueSrc)),
        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
        mThreadName(threadName) {
    ...
    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
        std::unique_lock<std::mutex> lock(mMutex);
        threadMain(lock);
    });
    ...
}

分析threadMain函數之前先看看EventThread.h中定義的幾種狀態

enum class State {
        Idle,   //空閒
        Quit,   //退出
        SyntheticVSync, 
        VSync,
    };
    //默認值爲Idle
    State mState GUARDED_BY(mMutex) = State::Idle;

SyntheticVSync和VSync狀態是互斥的:
mVSyncState->synthetic ? State::SyntheticVSync : State::VSync

mVSyncState->synthetic會在手機亮屏是被賦值爲false,滅屏時賦值爲true
threadMain比較複雜,主要作用是處理VSync的分發,它其實是一個死循環,只要線程沒有退出

void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
    DisplayEventConsumers consumers;

    while (mState != State::Quit) {
        ...
        if (!mPendingEvents.empty()) {
            event = mPendingEvents.front();
            ....

        if (!consumers.empty()) {
            dispatchEvent(*event, consumers);
            consumers.clear();
        }

        ...

        // Wait for event or client registration/request.
        if (mState == State::Idle) {
            mCondition.wait(lock);
        } else {
             ...
        }
    }
}

我這裏不詳細分析這個函數,後面再寫一篇文章分析,threadMain這個函數大致功能就是我留下的部分代碼:
如果mPendingEvents不爲空,就獲取其頭部事件,然後尋找能夠消耗此事件的EventThreadConnection,找到了就調用dispatchEvent分發事件,如果mState爲Idle,就讓此線程陷入等待,喚醒條件就是調用notify函數

dispatchEvent分發VSync的流程其實也很簡單,最終就是調用gui::BitTube::sendObjects函數

ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
    const char* vaddr = reinterpret_cast<const char*>(events);
    ssize_t size = tube->write(vaddr, count * objSize);
    ......
    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
ssize_t BitTube::write(void const* vaddr, size_t size) {
    ssize_t err, len;
    do {
        len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
        // cannot return less than size, since we're using SOCK_SEQPACKET
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    return err == 0 ? len : -err;
}

sendObjects和recvObjects對應的,sendObjects函數再調用內部write函數,write函數就是向mSendFd中寫數據,mSendFd和mReceiveFd對應一對可連接的socket,並且前一篇文章我們知道了nativeInit過程中mReceiveFd會被添加到handler的epoll中進行監聽,可參考AndroidQ Handler消息機制(native層),所以當mSendFd寫入數據之後,對應的mReceiveFd就能收到,並且會調用handleEvent回調函數,這個回調是在添加mReceiveFd時一併註冊的:

status_t DisplayEventDispatcher::initialize() {
    ...
    int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
            this, NULL);
    if (rc < 0) {
        return UNKNOWN_ERROR;
    }
    return OK;
}

此回調的實現就在DisplayEventDispatcher中:

int DisplayEventDispatcher::handleEvent(int, int events, void*) {
    ...
    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
                ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
                this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
        mWaitingForVsync = false;
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
    }

    return 1; // keep the callback
}

processPendingEvents這個函數前面已經分析過了,主要作用就是通過mReceiver.getEvents獲取事件,mReceiver類型爲DisplayEventReceiver,然後處理事件,獲取到的事件放入大小爲100的buf中,mReceiver.getEvents最終是通過gui::BitTube::recvObjects函數獲取mReceiveFd接收到的Vsync事件,並且只會處理最新的一次Vsync事件

事件收到之後通過dispatchVsync進行分發,dispatchVsync的實現在DisplayEventDispatcher子類NativeDisplayEventReceiver中:

void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
                                               uint32_t count) {
    ......
        env->CallVoidMethod(receiverObj.get(),
                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
       ......
}

這裏通過JNI調用java層DisplayEventReceiver的dispatchVsync方法就將Vsync信號相關信息發送到了應用層:

 private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        onVsync(timestampNanos, physicalDisplayId, frame);
    }

然後回調了onVsync方法,到這裏整個Vsync信號向native層註冊與應用層接收的整個流程就已經連通了,核心就是一對互連的socket,然後通過C++ 11提供的std::condition_variable條件變量的喚醒/等待機制來完成Vsync的註冊與監聽

應用層向native層請求Vsync信號的實質就是通過std::condition_variable喚醒當前處於wait狀態的EventThread,被喚醒之後就會處理mPendingEvents中的Vsync信號,然後分發,分發的實質就是通過gui::BitTube::sendObjects向mSendFd寫入數據,然後通過handler監聽的mReceiveFd就能收到數據並調用回調函數handleEvent,此回調中就將Vsync信息分發到了應用層,並調用onVsync方法開始處理View的繪製工作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章