[Android禪修之路] SurfaceFlinger合成總覽

語言: CN / TW / HK

theme: channing-cyan

SurfaceFlinger合成總覽

Android禪修之路

一 概述

SurfaceFlinger 所做的最主要的工作, 就是圖元的合成和顯示, 這所做的一切都是由系統的 Vsync 訊號控制的, 關於 Vsync 訊號本篇暫不研究, 本篇會對 SurfaceFlinger 接收到重新整理的訊息後的具體處理邏輯展開說明,這也是 SurfaceFlinger 的合成引擎所做的最主要的事情。

首先,還是從我們最熟悉的介面重新整理開始說起。如果我們的手機想要重新整理,會執行什麼邏輯呢,因為重新整理的觸發邏輯和 Vsync 相關,所以我們這裡直接看 SurfaceFlinger 接收到重新整理訊息之後的執行邏輯。

二 SurfaceFlinger的訊息處理

首先,我們看 SurfaceFlinger 的訊息處理函式 onMessageReceived,這裡只有兩種訊息型別,所以邏輯也比較簡單。

  • INVALIDATE:重繪訊息
  • REFRESH:重新整理訊息

cpp [frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp] /** * SurfaceFlinger收到訊息後的處理邏輯 * 主要訊息有INVALIDATE和REFRESH */ void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {    ATRACE_CALL();    switch (what) {        case MessageQueue::INVALIDATE: {            //處理INVALIDATE事件           ...            break;       }        case MessageQueue::REFRESH: {            //處理REFRESH事件            handleMessageRefresh();            break;       }   } }

這兩個事件我們都比較熟悉,一個是 INVALIDATE 的重繪事件,一個是 REFRESH 的重新整理事件,其中 INVALIDATE 事件最後還是會引發 REFRESH 事件,所以為了瀏覽整個流程, 我們先從 INVALIDATE 事件開始看起

2.1 SurfaceFlinger的INVALIDATE事件

```cpp //SurfaceFlinger INVALIDATE事件的邏輯 case MessageQueue::INVALIDATE: {    //首先判斷是否丟幀    bool frameMissed = previousFrameMissed();    //記錄具體的丟幀原因    bool hwcFrameMissed = mHadDeviceComposition && frameMissed;    bool gpuFrameMissed = mHadClientComposition && frameMissed;    if (frameMissed) {        mFrameMissedCount++;        mTimeStats->incrementMissedFrames();   }    if (hwcFrameMissed) {        mHwcFrameMissedCount++;   }    if (gpuFrameMissed) {        mGpuFrameMissedCount++;   } //丟幀的邏輯結束後,呼叫updateFpsBasedOnContent    if (mUseSmart90ForVideo) {        //這個是Fps視訊檢測功能        mScheduler->updateFpsBasedOnContent();   } ​    if (performSetActiveConfig()) {        break;   }

//如果丟幀,則不處理此次VSYNC

if (frameMissed && mPropagateBackpressure) {        if ((hwcFrameMissed && !gpuFrameMissed) ||            mPropagateBackpressureClientComposition) {            signalLayerUpdate();            break;       }   } ​    //呼叫Vr顯示    updateVrFlinger(); ​    //呼叫handleMessageTransaction    bool refreshNeeded = handleMessageTransaction();    //呼叫handleMessageInvalidate    refreshNeeded |= handleMessageInvalidate(); ​    updateCursorAsync();    updateInputFlinger(); ​    refreshNeeded |= mRepaintEverything;    if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {        //如果事務修改了視窗狀態、新緩衝區被鎖存或 HWC 已請求完全重繪,則發出重新整理訊號        signalRefresh();   }   break; } ```

到此, 我們可以瀏覽整個 INVALIDATE 事件的流程, 發現最後它還是會執行重新整理的邏輯, 只不過中執行重新整理邏輯之前, 做了兩件比較重要的事情 * handleMessageTransaction * handleMessageInvalidate

三 重新整理之前的工作

3.1 handleMessageTransaction

handleMessageTransaction 實際上就是呼叫了 handleTransaction 進行事務的處理。

cpp bool SurfaceFlinger::handleMessageTransaction() {    //peekTransactionFlags 函式是拿到 mTransactionFlags,這個標誌會在接收到一些事務的時候發生改變    uint32_t transactionFlags = peekTransactionFlags(); ​    bool flushedATransaction = flushTransactionQueues(); ​    bool runHandleTransaction = transactionFlags &&           ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction); ​ // 先看是否有需要處理的事務,如果有的話就執行 handleTransaction // 如果沒有呼叫 eTransactionFlushNeeded 修改 mTransactionFlags 的值    if (runHandleTransaction) {        handleTransaction(eTransactionMask);   } else {        getTransactionFlags(eTransactionFlushNeeded);   } ​ // 如果 mTransactionQueues 不為空,就需要需要處理事務,修改 mTransactionFlags 的值    if (transactionFlushNeeded()) {        setTransactionFlags(eTransactionFlushNeeded);   } ​    return runHandleTransaction; } ​ bool SurfaceFlinger::transactionFlushNeeded() {    return !mTransactionQueues.empty(); }

我們先看具體處理事務的邏輯。

3.1.1 handleTransaction

cpp void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { // 用 mDrawingState 建立一個副本 drawingState    State drawingState(mDrawingState); ​    Mutex::Autolock _l(mStateLock);    mDebugInTransaction = systemTime(); ​    mVsyncModulator.onTransactionHandled();    transactionFlags = getTransactionFlags(eTransactionMask); // 呼叫 handleTransactionLocked 這個是真正處理事務的函式    handleTransactionLocked(transactionFlags); ​    mDebugInTransaction = 0;    invalidateHwcGeometry(); } ​

3.1.2 handleTransactionLocked

handleTransactionLocked 這段程式碼很長,但是核心邏輯就只有兩塊

  1. 圖層是否發生了改變
  2. 顯示裝置是否發生了改變

針對以上兩塊邏輯,它對圖層進行了遍歷,判斷了圖層的屬性是否變化,並判斷了圖層所屬的顯示裝置是否在 mDisplays 集合中,最後處理了被移除的圖層,然後提交了事務。

cpp ​ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {    // 通知所有的圖層有可用幀    mCurrentState.traverseInZOrder([](Layer* layer) {        layer->notifyAvailableFrames();   }); ​ // 遍歷子物件(如果需要,為每個子物件執行事務)    if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {        mCurrentState.traverseInZOrder([&](Layer* layer) {         // 獲取圖層的 TransactionFlags            uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);            if (!trFlags) return; // 呼叫圖層的 doTransaction            const uint32_t flags = layer->doTransaction(0);            if (flags & Layer::eVisibleRegion)             // 如果返回的值是需要計算可見區域,那麼 mVisibleRegionsDirty 就設為 true                mVisibleRegionsDirty = true; ​            if (flags & Layer::eInputInfoChanged) {                mInputInfoChanged = true;           }       });        mTraversalNeededMainThread = false;   } ​    // 如果需要,執行顯示自己的事務    if (transactionFlags & eDisplayTransactionNeeded) {        processDisplayChangesLocked();        processDisplayHotplugEventsLocked();   } ​ // 如果顯示裝置發生了改變或者有顯示裝置的事務需要處理    if (transactionFlags & (eDisplayLayerStackChanged|eDisplayTransactionNeeded)) {        sp<const DisplayDevice> hintDisplay;        uint32_t currentlayerStack = 0;        bool first = true;        mCurrentState.traverseInZOrder([&](Layer* layer) {         // layerStack 是一個 uint32_t 的值,它表示當前的圖層關聯的圖層堆疊            uint32_t layerStack = layer->getLayerStack();            if (first || currentlayerStack != layerStack) {             // 如果是第一個圖層,或者是遍歷時 Layer 的圖層堆疊並不相同                currentlayerStack = layerStack;             // 確定此layerstack是否已映象(多個顯示)。如果是映象就選擇預設顯示;如果不是映象就選擇唯一顯示。                hintDisplay = nullptr;                for (const auto& [token, display] : mDisplays) {                    if (display->getCompositionDisplay()                                ->belongsInOutput(layer->getLayerStack(),                                                  layer->getPrimaryDisplayOnly())) {                        if (hintDisplay) {                            hintDisplay = nullptr;                            break;                       } else {                            hintDisplay = display;                       }                   }               }           } ​            if (!hintDisplay) {             // 這裡是一段修復bug的程式碼             // 當該層使用在任何顯示器上都不可見的 layerStack 時,可能為空。可能在螢幕關閉/開啟時發生                hintDisplay = getDefaultDisplayDeviceLocked();           }            if (hintDisplay) {                layer->updateTransformHint(hintDisplay);           } ​            first = false;       });   } ​ // 如果需要,執行我們自己的交易    if (mLayersAdded) {        mLayersAdded = false;        mVisibleRegionsDirty = true;   } ​ // 對於某些圖層已被刪除,需要更新它們所在的可見區域    if (mLayersRemoved) {        mLayersRemoved = false;        mVisibleRegionsDirty = true;        mDrawingState.traverseInZOrder([&](Layer* layer) {         // 屬於刪除圖層            if (mLayersPendingRemoval.indexOf(layer) >= 0) {                // 刪除不可見的圖層                Region visibleReg;                visibleReg.set(layer->getScreenBounds());                invalidateLayerStack(layer, visibleReg);           }       });   } ​ // 提交事務    commitInputWindowCommands();    commitTransaction(); }

3.1.3 提交事務

提交事務有兩個函式,commitInputWindowCommands 和 commitTransaction

commitInputWindowCommands

cpp [frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp] void SurfaceFlinger::commitInputWindowCommands() {    mInputWindowCommands = mPendingInputWindowCommands;    mPendingInputWindowCommands.clear(); }

commitInputWindowCommands 這個函式就是做了一個指令的賦值。

commitTransaction

cpp void SurfaceFlinger::commitTransaction() { // 先移除待移除的圖層    if (!mLayersPendingRemoval.isEmpty()) {        // 通知刪除的圖層不要進行繪製        for (const auto& l : mLayersPendingRemoval) {            recordBufferingStats(l->getName().string(),                    l->getOccupancyHistory(true)); ​         // 確保釋放設定為在任何顯示裝置上的所有緩衝區            if (l->isRemovedFromCurrentState()) {                latchAndReleaseBuffer(l);           }         // 如果該層已被刪除且沒有父層,則在螢幕上遍歷層時將無法訪問該層。         // 將圖層新增到 offscreenLayers 集合中,以確保可以將其當前狀態複製到繪圖狀態。            if (!l->getParent()) {                mOffscreenLayers.emplace(l.get());           }       }        mLayersPendingRemoval.clear();   } // 如果此事務是視窗動畫的一部分,那麼我們合成的下一幀也應視為動畫    mAnimCompositionPending = mAnimTransactionPending; ​    withTracingLock([&]() {     // 切換顯示狀態,將當前的狀態賦值給繪圖的狀態        mDrawingState = mCurrentState;        // 清楚改變的標記        mCurrentState.colorMatrixChanged = false; ​        mDrawingState.traverseInZOrder([&](Layer* layer) {            layer->commitChildList(); ​         // 如果在遍歷 mDrawingState 時可以到達該層,則該層不再是螢幕外的。從offscreenLayer集合中刪除該層            if (mOffscreenLayers.count(layer)) {                mOffscreenLayers.erase(layer);           }       }); ​        commitOffscreenLayers();   }); ​    mTransactionPending = false;    mAnimTransactionPending = false;    mTransactionCV.broadcast(); }

3.2 handleMessageInvalidate

cpp bool SurfaceFlinger::handleMessageInvalidate() { //呼叫 handlePageFlip,返回是否需要重新整理    bool refreshNeeded = handlePageFlip(); ​    if (mVisibleRegionsDirty) {     //計算 Layer 的邊界        computeLayerBounds();   } ​ // 在 handlePageFlip 中加進去的圖層    for (auto& layer : mLayersPendingRefresh) {        Region visibleReg;        visibleReg.set(layer->getScreenBounds());        invalidateLayerStack(layer, visibleReg);   }    mLayersPendingRefresh.clear();    return refreshNeeded; }

3.2.1 computeLayerBounds

cpp void SurfaceFlinger::computeLayerBounds() { for (const auto& pair : mDisplays) { const auto& displayDevice = pair.second; const auto display = displayDevice->getCompositionDisplay(); for (const auto& layer : mDrawingState.layersSortedByZ) { if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) { continue; } layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform()); } } } 這裡呼叫了 Layer 的 computeBounds 來計算它的邊界。

3.2.2 handlePageFlip

handlePageFlip 的字面意思就是翻頁,它實際上就是看看新的一頁有哪些資料需要處理(測量,重繪等等),並返回一個布林變數來決定後續是否需要重新整理。 cpp // 從各 Layer 的 BufferQueue 拿到最新的緩衝資料,並根據內容更新髒區域 bool SurfaceFlinger::handlePageFlip() {    nsecs_t latchTime = systemTime();    bool visibleRegions = false;    bool frameQueued = false;    bool newDataLatched = false; ​    // 儲存需要更新的Layer,當緩衝區被鎖存時,這個集合不能改變,因為這可能導致死鎖。    mDrawingState.traverseInZOrder([&](Layer* layer) {     // 返回 layer 是否有準備好的幀        if (layer->hasReadyFrame()) {            frameQueued = true;            nsecs_t expectedPresentTime;            expectedPresentTime = mScheduler->expectedPresentTime();         // 如果有了準備好的幀,還需要判斷這個幀是否需要當前顯示,         // Layer 會根據當前幀的時間還有自己期望的顯示時間來判斷自己當前是否需要顯示準備好的幀            if (layer->shouldPresentNow(expectedPresentTime)) {             // 如果需要顯示,就把這個圖層新增進佇列中                mLayersWithQueuedFrames.push_back(layer);           } else {             // 如果沒有準備好,或者不需要當前顯示,就不做顯示邏輯                layer->useEmptyDamage();           }       } else {            layer->useEmptyDamage();       }   }); ​ // 如果需要顯示的佇列不為空    if (!mLayersWithQueuedFrames.empty()) {        Mutex::Autolock lock(mStateLock);        for (auto& layer : mLayersWithQueuedFrames) {         // 每次重新繪製螢幕時呼叫 latchBuffer,並返回是否需要重新計算可見區域         //(這是一個相當繁重的操作,因此只有在需要時才應設定)。通常,這用於確定 Surface 的內容或大小是否已更改            if (layer->latchBuffer(visibleRegions, latchTime)) {             // 如果需要重新計算,就將圖層新增進 mLayersPendingRefresh 集合中                mLayersPendingRefresh.push_back(layer);           }            layer->useSurfaceDamage();            if (layer->isBufferLatched()) {                newDataLatched = true;           }       }   } ​    mVisibleRegionsDirty |= visibleRegions;    //如果我們需要在未來某個時間喚醒以處理不應在此 vsync 週期內顯示的排隊幀,請在下一個 vsync 週期喚醒以再次檢查。    if (frameQueued && (mLayersWithQueuedFrames.empty() || !newDataLatched)) {     // 傳遞圖層的重繪事件        signalLayerUpdate();   } ​    // 如果是 BOOTLOADER,就啟動 Boot 動畫    if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {        mBootStage = BootStage::BOOTANIMATION;   } ​    // 僅在確實有新工作要做時才需要繼續重新整理    return !mLayersWithQueuedFrames.empty() && newDataLatched; } ​ void SurfaceFlinger::signalLayerUpdate() {    mScheduler->resetIdleTimer();    mEventQueue->invalidate(); }

四 handleMessageRefresh

最後終於到了 handleMessageRefresh,就是 SurfaceFlinger 做的正式的重新整理工作了。雖然說是重新整理,但是它依然分了幾個階段進行

  • SurfaceFlinger 合成前的預處理
  • SurfaceFlinger 合作中的工作
  • SurfaceFlinger 合成後的掃尾工作

關於這三個部分,之後再詳細說明,這裡就先簡單看一下程式碼。

```cpp void SurfaceFlinger::handleMessageRefresh() {    mRefreshPending = false; ​    const bool repaintEverything = mRepaintEverything.exchange(false);    // 合成前預處理    preComposition();    // 重建Layer集合,並計算每個Layer的可見區域的髒資料    rebuildLayerStacks(); // 計算工作區間    calculateWorkingSet();  

for (const auto& [token, display] : mDisplays) {        beginFrame(display);        prepareFrame(display);        doDebugFlashRegions(display, repaintEverything);     // 合成中的工作        doComposition(display, repaintEverything);   } ​ // 合成後的工作    logLayerStats(); ​    postFrame();    postComposition(); ​    mHadClientComposition = false;    mHadDeviceComposition = false;    for (const auto& [token, displayDevice] : mDisplays) {        auto display = displayDevice->getCompositionDisplay();        const auto displayId = display->getId();        mHadClientComposition =                mHadClientComposition || getHwComposer().hasClientComposition(displayId);        mHadDeviceComposition =                mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);   } ​    mVsyncModulator.onRefreshed(mHadClientComposition);    mLayersWithQueuedFrames.clear(); } ```

五 總結

SurfaceFlinger 的合成工作極其複雜,本篇本來只是打算簡單的介紹一下 SurfaceFlinger 的合成流程,沒有想到就介紹幾個合成前的函式,篇幅就已經不少了,之後 SurfaceFlinger 的具體合成過程涉及到的東西只會更多。

最後還是做一個簡單的總結

  1. SurfaceFlinger 會通過它的訊息回撥來接收兩種事務,它們分別是 INVALIDATE 和 REFRESH,而 INVALIDATE 事件最後還是會走到 REFRESH 事件的邏輯。
  2. 在 INVALIDATE 中 SurfaceFlinger 會遍歷所有的圖層,判斷哪些圖層需要重新測量,哪些圖層需要重新整理,並判斷圖層的改變和顯示裝置的改變,最後呼叫 handlePageFlip 來決定是否需要重新整理
  3. handleMessageRefresh 是 SurfaceFlinger 的重新整理邏輯