Android Systrace 基礎知識(5) -- SurfaceFlinger 解讀

本文是 Android Systrace 系列文章的第五篇,主要是對 Android 系統中的 SurfaceFlinger 進行簡單介紹,介紹了 SurfaceFlinger 中幾個比較重要的線程,包括 Vsync 信號的解讀、應用的 Buffer 展示、卡頓判定等,由於 Vsync 這一塊在Systrace 基礎知識 - Vsync 解讀Android 基於 Choreographer 的渲染機制詳解 這兩篇文章裏面已經介紹過,這裏就不再做詳細的講解了。

本系列的目的是通過 Systrace 這個工具,從另外一個角度來看待 Android 系統整體的運行,同時也從另外一個角度來對 Framework 進行學習。也許你看了很多講 Framework 的文章,但是總是記不住代碼,或者不清楚其運行的流程,也許從 Systrace 這個圖形化的角度,你可以理解的更深入一些。

系列文章目錄

  1. Systrace 簡介
  2. Systrace 基礎知識 - Systrace 預備知識
  3. Systrace 基礎知識 - Why 60 fps ?
  4. Systrace 基礎知識 - SystemServer 解讀
  5. Systrace 基礎知識 - SurfaceFlinger 解讀
  6. Systrace 基礎知識 - Input 解讀
  7. Systrace 基礎知識 - Vsync 解讀
  8. Systrace 基礎知識 - Vsync-App :基於 Choreographer 的渲染機制詳解
  9. Systrace 基礎知識 - MainThread 和 RenderThread 解讀
  10. Systrace 基礎知識 - Binder 和鎖競爭解讀
  11. Systrace 基礎知識 - Triple Buffer 解讀
  12. Systrace 基礎知識 - CPU Info 解讀

正文

這裏直接上官方對於 SurfaceFlinger 流程的定義

  1. 大多數應用在屏幕上一次顯示三個層:屏幕頂部的狀態欄、底部或側面的導航欄以及應用界面。有些應用會擁有更多或更少的層(例如,默認主屏幕應用有一個單獨的壁紙層,而全屏遊戲可能會隱藏狀態欄)。每個層都可以單獨更新。狀態欄和導航欄由系統進程渲染,而應用層由應用渲染,兩者之間不進行協調。
  2. 設備顯示會按一定速率刷新,在手機和平板電腦上通常爲 60 fps。如果顯示內容在刷新期間更新,則會出現撕裂現象;因此,請務必只在週期之間更新內容。在可以安全更新內容時,系統便會收到來自顯示設備的信號。由於歷史原因,我們將該信號稱爲 VSYNC 信號。
  3. 刷新率可能會隨時間而變化,例如,一些移動設備的幀率範圍在 58 fps 到 62 fps 之間,具體要視當前條件而定。對於連接了 HDMI 的電視,刷新率在理論上可以下降到 24 Hz 或 48 Hz,以便與視頻相匹配。由於每個刷新週期只能更新屏幕一次,因此以 200 fps 的幀率爲顯示設備提交緩衝區就是一種資源浪費,因爲大多數幀會被捨棄掉。SurfaceFlinger 不會在應用每次提交緩衝區時都執行操作,而是在顯示設備準備好接收新的緩衝區時纔會喚醒。
  4. 當 VSYNC 信號到達時,SurfaceFlinger 會遍歷它的層列表,以尋找新的緩衝區。如果找到新的緩衝區,它會獲取該緩衝區;否則,它會繼續使用以前獲取的緩衝區。SurfaceFlinger 必須始終顯示內容,因此它會保留一個緩衝區。如果在某個層上沒有提交緩衝區,則該層會被忽略。
  5. SurfaceFlinger 在收集可見層的所有緩衝區之後,便會詢問 Hardware Composer 應如何進行合成。」

---- 引用自SurfaceFlinger 和 Hardware Composer

下面是上述流程所對應的流程圖, 簡單地說, SurfaceFlinger 最主要的功能:SurfaceFlinger 接受來自多個來源的數據緩衝區,對它們進行合成,然後發送到顯示設備。

SurfaceFlinger 的工作流程
SurfaceFlinger 的工作流程

那麼 Systrace 中,我們關注的重點就是上面這幅圖對應的部分

  1. App 部分
  2. BufferQueue 部分
  3. SurfaceFlinger 部分
  4. HWComposer 部分

這四部分,在 Systrace 中都有可以對應的地方,以時間發生的順序排序就是 1、2、3、4,下面我們從 Systrace 的這四部分來看整個渲染的流程

App 部分

關於 App 部分,其實在Systrace 基礎知識 - MainThread 和 RenderThread 解讀這篇文章裏面已經說得比較清楚了,不清楚的可以去這篇文章裏面看,其主要的流程如下圖:

App 一幀的流程
App 一幀的流程

從 SurfaceFlinger 的角度來看,App 部分主要負責生產 SurfaceFlinger 合成所需要的 Surface。

App 與 SurfaceFlinger 的交互主要集中在三點

  1. Vsync 信號的接收和處理
  2. RenderThread 的 dequeueBuffer
  3. RenderThread 的 queueBuffer

Vsync 信號的接收和處理

關於這部分內容可以查看Android 基於 Choreographer 的渲染機制詳解 這篇文章,App 和 SurfaceFlinger 的第一個交互點就是 Vsync 信號的請求和接收,如上圖中第一條標識,Vsync-App 信號到達,就是指的是 SurfaceFlinger 的 Vsync-App 信號。應用收到這個信號後,開始一幀的渲染準備

Vsync 信號的接收和處理
Vsync 信號的接收和處理

RenderThread 的 dequeueBuffer

dequeue 有出隊的意思,dequeueBuffer 顧名思義,就是從隊列中拿出一個 Buffer,這個隊列就是 SurfaceFlinger 中的 BufferQueue。如下圖,應用開始渲染前,首先需要通過 Binder 調用從 SurfaceFlinger 的 BufferQueue 中獲取一個 Buffer,其流程如下:

App 端的 Systrace 如下所示

App 端的 Buffer 操作
App 端的 Buffer 操作

SurfaceFlinger 端的 Systrace 如下所示

SurfaceFlinger 端的 Buffer 操作
SurfaceFlinger 端的 Buffer 操作

RenderThread 的 queueBuffer

queue 有入隊的意思,queueBuffer 顧名思義就是講 Buffer 放回到 BufferQueue,App 處理完 Buffer 後(寫入具體的 drawcall),會把這個 Buffer 通過 eglSwapBuffersWithDamageKHR -> queueBuffer 這個流程,將 Buffer 放回 BufferQueue,其流程如下

App 端的 Systrace 如下所示 App 端的 queueBuffer 操作

SurfaceFlinger 端的 Systrace 如下所示 SurfaceFlinger 端的 queueBuffer 操作

通過上面三部分,大家應該對下圖中的流程會有一個比較直觀的瞭解了

BufferQueue 部分

BufferQueue 部分其實在Systrace 基礎知識 - Triple Buffer 解讀 這裏有講,如下圖,結合上面那張圖,每個有顯示界面的進程對應一個 BufferQueue,使用方創建並擁有 BufferQueue 數據結構,並且可存在於與其生產方不同的進程中,BufferQueue 工作流程如下:

BufferQueue 的工作流程
BufferQueue 的工作流程

上圖主要是 dequeue、queue、acquire、release ,在這個例子裏面,App 是生產者,負責填充顯示緩衝區(Buffer);SurfaceFlinger 是消費者,將各個進程的顯示緩衝區做合成操作

  1. dequeue(生產者發起) : 當生產者需要緩衝區時,它會通過調用 dequeueBuffer() 從 BufferQueue 請求一個可用的緩衝區,並指定緩衝區的寬度、高度、像素格式和使用標記。
  2. queue(生產者發起):生產者填充緩衝區並通過調用 queueBuffer() 將緩衝區返回到隊列。
  3. acquire(消費者發起) :消費者通過 acquireBuffer() 獲取該緩衝區並使用該緩衝區的內容
  4. release(消費者發起) :當消費者操作完成後,它會通過調用 releaseBuffer() 將該緩衝區返回到隊列

SurfaceFlinger 部分

工作流程

從最前面我們知道 SurfaceFlinger 的主要工作就是合成:

當 VSYNC 信號到達時,SurfaceFlinger 會遍歷它的層列表,以尋找新的緩衝區。如果找到新的緩衝區,它會獲取該緩衝區;否則,它會繼續使用以前獲取的緩衝區。SurfaceFlinger 必須始終顯示內容,因此它會保留一個緩衝區。如果在某個層上沒有提交緩衝區,則該層會被忽略。SurfaceFlinger 在收集可見層的所有緩衝區之後,便會詢問 Hardware Composer 應如何進行合成。

其 Systrace 主線程可用看到其主要是在收到 Vsync 信號後開始工作

SurfaceFlinger 流程
SurfaceFlinger 流程

其對應的代碼如下,主要是處理兩個 Message

  1. MessageQueue::INVALIDATE --- 主要是執行 handleMessageTransaction 和 handleMessageInvalidate 這兩個方法
  2. MessageQueue::REFRESH --- 主要是執行 handleMessageRefresh 方法

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
    ATRACE_CALL();
    switch (what) {
        case MessageQueue::INVALIDATE: {
            ......
            bool refreshNeeded = handleMessageTransaction();
            refreshNeeded |= handleMessageInvalidate();
            ......
            break;
        }
        case MessageQueue::REFRESH: {
            handleMessageRefresh();
            break;
        }
    }
}

//handleMessageInvalidate 實現如下
bool SurfaceFlinger::handleMessageInvalidate() {
    ATRACE_CALL();
    bool refreshNeeded = handlePageFlip();

    if (mVisibleRegionsDirty) {
        computeLayerBounds();
        if (mTracingEnabled) {
            mTracing.notify("visibleRegionsDirty");
        }
    }

    for (auto& layer : mLayersPendingRefresh) {
        Region visibleReg;
        visibleReg.set(layer->getScreenBounds());
        invalidateLayerStack(layer, visibleReg);
    }
    mLayersPendingRefresh.clear();
    return refreshNeeded;
}

//handleMessageRefresh 實現如下, SurfaceFlinger 的大部分工作都是在handleMessageRefresh 中發起的
void SurfaceFlinger::handleMessageRefresh() {
    ATRACE_CALL();

    mRefreshPending = false;

    const bool repaintEverything = mRepaintEverything.exchange(false);
    preComposition();
    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();
}

handleMessageRefresh 中按照重要性主要有下面幾個功能

  1. 準備工作
    1. preComposition();
    2. rebuildLayerStacks();
    3. calculateWorkingSet();
  2. 合成工作
    1. begiFrame(display);
    2. prepareFrame(display);
    3. doDebugFlashRegions(display, repaintEverything);
    4. doComposition(display, repaintEverything);
  3. 收尾工作
    1. logLayerStats();
    2. postFrame();
    3. postComposition();

由於顯示系統有非常龐大的細節,這裏就不一一進行講解了,如果你的工作在這一部分,那麼所有的流程都需要熟悉並掌握,如果只是想熟悉流程,那麼不需要太深入,知道 SurfaceFlinger 的主要工作邏輯即可

掉幀

通常我們通過 Systrace 判斷應用是否掉幀的時候,一般是直接看 SurfaceFlinger 部分,主要是下面幾個步驟

  1. SurfaceFlinger 的主線程在每個 Vsync-SF 的時候是否沒有合成?
  2. 如果沒有合成操作,那麼需要看沒有合成的原因:
    1. 因爲 SurfaceFlinger 檢查發現沒有可用的 Buffer 而沒有合成操作?
    2. 因爲 SurfaceFlinger 被其他的工作佔用(比如截圖、HWC 等)?
  3. 如果有合成操作,那麼需要看對應的 App 的 可用 Buffer 個數是否正常:如果 App 此時可用 Buffer 爲 0,那麼看 App 端爲何沒有及時 queueBuffer(這就一般是應用自身的問題了),因爲 SurfaceFlinger 合成操作觸發可能是其他的進程有可用的 Buffer

關於這一部分的 Systrace 怎麼看,在 Systrace 基礎知識 - Triple Buffer 解讀-掉幀檢測 部分已經有比較詳細的解讀,大家可以過去看這一段

HWComposer 部分

關於 HWComposer 的功能部分我們就直接看官方的介紹即可

  1. Hardware Composer HAL (HWC) 用於確定通過可用硬件來合成緩衝區的最有效方法。作爲 HAL,其實現是特定於設備的,而且通常由顯示設備硬件原始設備製造商 (OEM) 完成。
  2. 當您考慮使用疊加平面時,很容易發現這種方法的好處,它會在顯示硬件(而不是 GPU)中合成多個緩衝區。例如,假設有一部普通 Android 手機,其屏幕方向爲縱向,狀態欄在頂部,導航欄在底部,其他區域顯示應用內容。每個層的內容都在單獨的緩衝區中。您可以使用以下任一方法處理合成(後一種方法可以顯著提高效率):
    1. 將應用內容渲染到暫存緩衝區中,然後在其上渲染狀態欄,再在其上渲染導航欄,最後將暫存緩衝區傳送到顯示硬件。
    2. 將三個緩衝區全部傳送到顯示硬件,並指示它從不同的緩衝區讀取屏幕不同部分的數據。
  3. 顯示處理器功能差異很大。疊加層的數量(無論層是否可以旋轉或混合)以及對定位和疊加的限制很難通過 API 表達。爲了適應這些選項,HWC 會執行以下計算(由於硬件供應商可以定製決策代碼,因此可以在每臺設備上實現最佳性能):
    1. SurfaceFlinger 向 HWC 提供一個完整的層列表,並詢問“您希望如何處理這些層?”
    2. HWC 的響應方式是將每個層標記爲疊加層或 GLES 合成。
    3. SurfaceFlinger 會處理所有 GLES 合成,將輸出緩衝區傳送到 HWC,並讓 HWC 處理其餘部分。
  4. 當屏幕上的內容沒有變化時,疊加平面的效率可能會低於 GL 合成。當疊加層內容具有透明像素且疊加層混合在一起時,尤其如此。在此類情況下,HWC 可以選擇爲部分或全部層請求 GLES 合成,並保留合成的緩衝區。如果 SurfaceFlinger 返回來要求合成同一組緩衝區,HWC 可以繼續顯示先前合成的暫存緩衝區。這可以延長閒置設備的電池續航時間。
  5. 運行 Android 4.4 或更高版本的設備通常支持 4 個疊加平面。嘗試合成的層數多於疊加層數會導致系統對其中一些層使用 GLES 合成,這意味着應用使用的層數會對能耗和性能產生重大影響。

-------- 引用自SurfaceFlinger 和 Hardware Composer

我們繼續接着看 SurfaceFlinger 主線程的部分,對應上面步驟中的第三步,下圖可以看到 SurfaceFlinger 與 HWC 的通信部分 HWC 與 SurfaceFlinger

這也對應了最上面那張圖的後面部分 HWC 與 SurfaceFlinger

不過這其中的細節非常多,這裏就不詳細說了。至於爲什麼要提 HWC,因爲 HWC 不僅是渲染鏈路上重要的一環,其性能也會影響整機的性能,Android 中的卡頓丟幀原因概述 - 系統篇 這篇文章裏面就有列有 HWC 導致的卡頓問題(性能不足,中斷信號慢等問題)

想了解更多 HWC 的知識,可以參考這篇文章Android P 圖形顯示系統(一)硬件合成HWC2,當然,作者的Android P 圖形顯示系這個系列大家可以仔細看一下

參考文章

  1. Android P 圖形顯示系統(一)硬件合成HWC2
  2. Android P 圖形顯示系統
  3. SurfaceFlinger 的定義
  4. surfacefliner

關於我 && 博客

  1. 關於我 , 非常希望和大家一起交流 , 共同進步 .
  2. 博客內容導航
  3. 優秀博客文章記錄 - Android 性能優化必知必會

一個人可以走的更快 , 一羣人可以走的更遠

本文使用 mdnice 排版

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