Andorid 硬件顯示系統HWC&HWC2架構詳解

硬件合成HWC2

Hardware Composer HAL (HWC) 是 SurfaceFlinger 用來將 Surface 合成到屏幕。HWC 可以抽象出疊加層和 2D 位塊傳送器等,其主要是幫忙GPU完成一些工作。

SurfaceFlinger是一個系統服務,其作用是接受來自多個源的Buffer數據,對它們進行合成,然後發送到顯示設備進行顯示。在之前的Android版本中,顯示基本都是基於硬件的FrameBuffer來實現的,例如/dev/graphics/fb0,但是在後來的版本中,實現可以多樣化了,比如高通採用SDM,其他有些平臺採用ADF,DRM等。

SurfaceFlinger和HWC的相關配合,實現Android系統的合成與顯示~

SurfaceFlinger概述

大多數應用通常在屏幕上有三個層:屏幕頂部的狀態欄、底部或側面的導航欄以及應用的界面。有些應用會擁有更多或更少的層(例如,默認主屏幕應用有一個單獨的壁紙層,而全屏遊戲可能會隱藏狀態欄)。每個層都可以單獨更新。狀態欄和導航欄由系統進程渲染,而應用層由應用渲染,兩者之間不進行協調。

下面我們通過一張圖,來進行說明:

 

Android顯示系統

從上圖,我們可以看出,在長按Power鍵,彈出關機對話框時,有4層Layer,可以立即爲有4個窗口。4個窗口經過SurfaceFlinger進行合成後,再送到顯示器進行顯示。

我們來看看SurfaceFlinger的類定義:

 

class SurfaceFlinger : public BnSurfaceComposer,
                       public PriorityDumper,
                       private IBinder::DeathRecipient,
                       private HWC2::ComposerCallback
{

SurfaceFlinger繼承BnSurfaceComposer,實現ISurfaceComposer接口。實現ComposerCallback。PriorityDumper是一個輔助類,主要提供SurfaceFlinger的信息dump,並提供信息的分離和格式設置。

  • ** ISurfaceComposer接口實現**
    ISurfaceComposer是提供給上層Client端的接口,ISurfaceComposer接口包括:

 

* frameworks/native/libs/gui/include/gui/ISurfaceComposer.h

    enum {
        // Note: BOOT_FINISHED must remain this value, it is called from
        // Java by ActivityManagerService.
        BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
        CREATE_CONNECTION,
        UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC
        CREATE_DISPLAY_EVENT_CONNECTION,
        CREATE_DISPLAY,
        DESTROY_DISPLAY,
        GET_BUILT_IN_DISPLAY,
        SET_TRANSACTION_STATE,
        AUTHENTICATE_SURFACE,
        GET_SUPPORTED_FRAME_TIMESTAMPS,
        GET_DISPLAY_CONFIGS,
        GET_ACTIVE_CONFIG,
        SET_ACTIVE_CONFIG,
        CONNECT_DISPLAY,
        CAPTURE_SCREEN,
        CAPTURE_LAYERS,
        CLEAR_ANIMATION_FRAME_STATS,
        GET_ANIMATION_FRAME_STATS,
        SET_POWER_MODE,
        GET_DISPLAY_STATS,
        GET_HDR_CAPABILITIES,
        GET_DISPLAY_COLOR_MODES,
        GET_ACTIVE_COLOR_MODE,
        SET_ACTIVE_COLOR_MODE,
        ENABLE_VSYNC_INJECTIONS,
        INJECT_VSYNC,
        GET_LAYER_DEBUG_INFO,
        CREATE_SCOPED_CONNECTION
    };

ISurfaceComposer的接口在SurfaceFlinger中都有對應的方法實現。Client端通過Binder調到SurfaceFlinger中。前面我們已經說過上層怎麼獲取Display的信息,其實現就是SurfaceFlinger中的getDisplayConfigs函數。其他的類似,根據上面Binder的command去找對應的實現。

  • ComposerCallback接口實現
    ComposerCallback是HWC2的callback接口,包括以下接口:

 

class ComposerCallback {
 public:
    virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
                                   Connection connection,
                                   bool primaryDisplay) = 0;
    virtual void onRefreshReceived(int32_t sequenceId,
                                   hwc2_display_t display) = 0;
    virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
                                 int64_t timestamp) = 0;
    virtual ~ComposerCallback() = default;
};

callback提供了註冊接口,registerCallback,在SurfaceFlinger初始化時,註冊:

 

void SurfaceFlinger::init() {
    ... ...

    LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
            "Starting with vr flinger active is not currently supported.");
    getBE().mHwc.reset(new HWComposer(getBE().mHwcServiceName));
    getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);

registerCallback時的this就是SurfaceFlinger對ComposerCallback接口的實現。

  • onHotplugReceived
    熱插拔事件的回調,顯示屏幕連接或斷開時回調。Surfaceflinger中實現的方法爲SurfaceFlinger::onHotplugReceived。這塊邏輯,我們稍後介紹。

  • onRefreshReceived
    接收底層HWComposer的刷新請求,實現方法如下:

 

void SurfaceFlinger::onRefreshReceived(int sequenceId,
                                       hwc2_display_t /*display*/) {
    Mutex::Autolock lock(mStateLock);
    if (sequenceId != getBE().mComposerSequenceId) {
        return;
    }
    repaintEverythingLocked();
}

void SurfaceFlinger::repaintEverythingLocked() {
    android_atomic_or(1, &mRepaintEverything);
    signalTransaction();
}

在repaintEverythingLocked中,注意這裏的mRepaintEverything,repaintEverythingLocked的值爲1。signalTransaction將觸發一次刷新,重新進行合成顯示。

重新繪製,說明底層配置,參數等有變動,SurfaceFlinger前面給的數據不能用,得重新根據變動後的配置進行進行合成,給適合當前配置的顯示數據。

  • onVsyncReceived
    Vsync事件上報,接收底層硬件上報的垂直同步信號。

需要注意的是,這裏收到的熱插拔和Vsync回調,又將被封裝爲事件的形式再通知出去,這是回話,後續介紹。

  • 顯示週期Vsync

設備顯示會按一定速率刷新,在手機和平板電腦上通常爲每秒 60 幀。如果顯示內容在刷新期間更新,則會出現撕裂現象;因此,請務必只在週期之間更新內容。在可以安全更新內容時,系統便會收到來自顯示設備的信號。由於歷史原因,我們將該信號稱爲 VSYNC 信號。

刷新率可能會隨時間而變化,例如,一些移動設備的刷新率範圍在 58 fps 到 62 fps 之間,具體要視當前條件而定。對於連接了 HDMI 的電視,刷新率在理論上可以下降到 24 Hz 或 48 Hz,以便與視頻相匹配。由於每個刷新週期只能更新屏幕一次,因此以 200 fps 的刷新率爲顯示設備提交緩衝區只是在做無用功,因爲大多數幀永遠不會被看到。SurfaceFlinger 不會在應用提交緩衝區時執行操作,而是在顯示設備準備好接收新的緩衝區時纔會喚醒。

當 VSYNC 信號到達時,SurfaceFlinger 會遍歷它的層列表,以尋找新的緩衝區。如果找到新的緩衝區,它會獲取該緩衝區;否則,它會繼續使用以前獲取的緩衝區。SurfaceFlinger 總是需要可顯示的內容,因此它會保留一個緩衝區。如果在某個層上沒有提交緩衝區,則該層會被忽略。

  • 合成方式

目前SurfaceFlinger中支持兩種合成方式,一種是Device合成,一種是Client合成。SurfaceFlinger 在收集可見層的所有緩衝區之後,便會詢問 Hardware Composer 應如何進行合成。

  • Client合成
    Client合成方式是相對與硬件合成來說的,其合成方式是,將各個Layer的內容用GPU渲染到暫存緩衝區中,最後將暫存緩衝區傳送到顯示硬件。這個暫存緩衝區,我們稱爲FBTarget,每個Display設備有各自的FBTarget。Client合成,之前稱爲GLES合成,我們也可以稱之爲GPU合成。Client合成,採用RenderEngine進行合成。

  • Device合成
    就是用專門的硬件合成器進行合成HWComposer,所以硬件合成的能力就取決於硬件的實現。其合成方式是將各個Layer的數據全部傳給顯示硬件,並告知它從不同的緩衝區讀取屏幕不同部分的數據。HWComposer是Devicehec的抽象。

所以,整個顯示系統的數據流如下圖所示,此圖來源於Androd 官網:

 

Android顯示系統數據流圖

GPU合成後數據,作爲一個特殊的Layer,傳給顯示硬件。

  • dump信息
    dump信息是很好的一個調試手段,dump命令 如下:

 

adb shell dumpsys SurfaceFlinger

比如,我們通過dump,就可以知道當前有那些Layer,都用什麼合成方式

 

Display 0 HWC layers:
-------------------------------------------------------------------------------
 Layer name
           Z |  Comp Type |   Disp Frame (LTRB) |          Source Crop (LTRB)
-------------------------------------------------------------------------------
 com.android.settings/com.android.settings.Settings#0
       21005 |     Client |    0    0  480  800 |    0.0    0.0  480.0  800.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 StatusBar#0
      181000 |     Client |    0    0  480   36 |    0.0    0.0  480.0   36.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

此時,有兩個Layer,採用Client合成方式。

  • 後端SurfaceFlingerBE
    SurfaceFlingerBE是Android P上新分離出來的,沒有太多信息,從目前的定義來看是將SurfaceFlinger分離爲前端和後端,這裏的SurfaceFlingerBE就是後端,現在的SurfaceFlinger充當前端的角色。後端SurfaceFlingerBE主要就是和底層合成打交道,前端和上層進行交互。在後續的版本中,更多的邏輯會被移到後端中。

  • 消息隊列和主線程
    和應用進程類似,SurfaceFlinger也有一個主線程,SurfaceFlinger的主線程主要進行顯示數據的處理,也就是合成。SurfaceFlinger是一個服務,將會響應上層的很多請求,各個進程的請求都在SurfaceFlinger的各個Binder線程中,如果線程很耗時,那麼應用端就會被block,顯示也會被block。主線程就是將他們分離開來,各幹各的事。

SurfaceFlinger還有很多邏輯,我們先來看看SurfaceFlinger相關的類,接下再來我們一下進行介紹。

 

SurfaceFlinger相關類圖

其他的都好理解,有兩個地方需要注意:
1.SurfaceFlinger有兩個狀態,Layer也有兩個狀態,一個mCurrentState,一個mDrawingState。注意這裏State類定義是不一樣的。
2.兩個EventThread,一個是給SurfaceFlinger本身用,一個是爲了給應用分發事件的。

 

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

VSyncSources 是Vsync的源,並不是每一個Vsync信號都是從底層硬件上報的,平時的Vsync都是VSyncSources分發出來的,VSyncSources定期會和底層的Vsync進行同步,確保和底層屏幕新的步調一致。

HWC2 概述

Android 7.0 包含新版本的 HWC (HWC2),Android需要自行配置,到Android 8.0,HWC2正式開啓,且版本升級爲2.1版本。HWC2是 SurfaceFlinger 用來與專門的窗口合成硬件進行通信。SurfaceFlinger 包含使用 3D 圖形處理器 (GPU) 執行窗口合成任務的備用路徑,但由於以下幾個原因,此路徑並不理想:

  • 通常,GPU 未針對此用例進行過優化,因此能耗可能要大於執行合成所需的能耗。
  • 每次 SurfaceFlinger 使用 GPU 進行合成時,應用都無法使用處理器進行自我渲染,因此應儘可能使用專門的硬件而不是 GPU 進行合成。

下面是GPU和HWC兩種方式的優劣對比:

合成類型 耗電情況 性能情況 Alpha處理 DRM內容處理 其他限制
Device合成(HWC) 耗電低 性能高 很多Vendor的HWC不支持Alpha的處理和合成 基本都能訪問DRM內容 能合成的Surface層數有限,對每種Surface類型處理層數有限
Client合成(GPU) 耗電高 性能低 能處理每個像素的Alpha及每個Layear的Alpha 早期版本GPU不能訪問DRM的內容 目前的處理層數沒有限制

所以,HWC的設計最好遵循一些基本的規則~

HWC 常規準則

由於 Hardware Composer 抽象層後的物理顯示設備硬件可因設備而異,因此很難就具體功能提供建議。一般來說,請遵循以下準則:

  • HWC 應至少支持 4 個疊加層(狀態欄、系統欄、應用和壁紙/背景)。
  • 層可以大於屏幕,因此 HWC 應能處理大於顯示屏的層(例如壁紙)。
  • 應同時支持預乘每像素 Alpha 混合和每平面 Alpha 混合。
  • HWC 應能處理 GPU、相機和視頻解碼器生成的相同緩衝區,因此支持以下某些屬性很有幫助:
    • RGBA 打包順序
    • YUV 格式
    • Tiling, swizzling和步幅屬性
  • 爲了支持受保護的內容,必須提供受保護視頻播放的硬件路徑。

Tiling,翻譯過來就沒有原文的意味了,說白了,就是將image進行切割,切成MxN的小塊,最後用的時候,再將這些小塊拼接起來,就像鋪瓷磚一樣。

swizzling,比Tiling難理解點,它是一種拌和技術,這是向量的單元可以被任意地重排或重複,見過的hwc代碼寫的都比較隱蔽,沒有見多處理的地方。

HWC專注於優化,智能地選擇要發送到疊加硬件的 Surface,以最大限度減輕 GPU 的負載。另一種優化是檢測屏幕是否正在更新;如果不是,則將合成委託給 OpenGL 而不是 HWC,以節省電量。當屏幕再次更新時,繼續將合成分載到 HWC。

爲常見用例做準備,如:

  • 縱向和橫向模式下的全屏遊戲
  • 帶有字幕和播放控件的全屏視頻
  • 主屏幕(合成狀態欄、系統欄、應用窗口和動態壁紙)
  • 受保護的視頻播放
  • 多顯示設備支持

HWC2 框架

從Android 8.0開始的Treble項目,對Android的架構做了重大的調整,讓製造商以更低的成本更輕鬆、更快速地將設備更新到新版 Android 系統。這就對 HAL 層有了很大的調整,利用提供給Vendor的接口,將Vendor的實現和Android上層分離開來。

這樣的架構讓HWC的架構也變的複雜,HWC屬於Binderized的HAL類型。Binderized類型的HAL,將上層Androd和底層HAL分別採用兩個不用的進程實現,中間採用Binder進行通信,爲了和前面的Binder進行區別,這裏採用HwBinder。

因此,我們可以將HWC再進行劃分,可以分爲下面這幾個部分(空間Space),如下圖:

 

HWC2實現

  • Client端
    Client也就是SurfaceFlinger,不過SurfaceFlinger採用前後端的設計,以後和HWC相關的邏輯應該都會放到SurfaceFlinger後端也就是SurfaceFlingerBE中。代碼位置:

 

frameworks/native/services/surfaceflinger
  • HWC2 Client端
    這一部分屬於SurfaceFlinger進程,其直接通過Binder通信,和HWC2的HAL Server交互。這部分的代碼也在SurfaceFlinger進程中,但是採用Hwc2的命名空間。

  • HWC Server端
    這一部分還是屬於Android的系統,這裏將建立一個進程,實現HWC的服務端,Server端再調底層Vendor的具體實現。並且,對於底層合成的實現不同,這裏會做一些適配,適配HWC1.x,和FrameBuffer的實現。這部分包含三部分:接口,實現和服務,以動態庫的形式存在:

 

[email protected]
[email protected]
[email protected]

代碼位置:

 

hardware/interfaces/graphics/composer/2.1/default
  • HWC Vendor的實現
    這部分是HWC的具體實現,這部分的實現由硬件廠商完成。比如高通平臺,代碼位置一般爲:

 

hardware/qcom/display

需要注意的是,HWC必須採用Binderized HAL模式,但是,並沒有要求一定要實現HWC2的HAL版本。HWC2的實現需要配置,以Android 8.0爲例:

  1. 添加宏定義 TARGET_USES_HWC2
  2. 編譯打包HWC2相關的so庫
  3. SeLinux相關的權限添加
  4. 配置manifest.xml

 

     <hal format="hidl">
        <name>android.hardware.graphics.composer</name>
        <transport>hwbinder</transport>
        <version>2.1</version>
        <interface>
            <name>IComposer</name>
            <instance>default</instance>
        </interface>
    </hal>

基於上面的劃分,我們來看看HWC2相關的類圖:

 

HWC2相關類圖

下面我們將詳細來看!

HWC2 數據結構

HWC2 中提供了幾個數據結構來描述合成以及可以顯示設備的就交互,比如圖層(Layer),顯示屏(Display)。HWC2的一些常用接口定義在頭文件hwcomposer2.h中:

 

hardware/libhardware/include/hardware
    ├── hwcomposer2.h
    ├── hwcomposer_defs.h
    └── hwcomposer.h

 

hardware/interfaces/graphics/composer/2.1/types.hal

另外一些共用的數據定義是HAL的接口中:

 

hardware/interfaces/graphics/common/1.0
    ├── Android.bp
    └── types.hal

當然,SurfaceFlinger中有很多相關的數據結構:

 

frameworks/native/services/surfaceflinger

圖層Layer

圖層(Layer)是合成的最重要單元;每個圖層都有一組屬性,用於定義它與其他層的交互方式。Layer在每一層中的代碼的實現不一樣,基本上Laye的理念都是一樣的。

SurfaceFlinger中定義了Layer,相關的類如下:

 

frameworks/native/services/surfaceflinger
├── Layer.h
├── Layer.cpp
├── ColorLayer.h
├── ColorLayer.cpp
├── BufferLayer.h
└── BufferLayer.cpp
  

HWC2中定義了HWC2::Layer:

 

frameworks/native/services/surfaceflinger/DisplayHardware
├── HWC2.h
└── HWC2.cpp

在HAL中實現時,定義爲hwc2_layer_t,這是在頭文件hwcomposer2.h中定義的

 

typedef uint64_t hwc2_layer_t;

HIDL中定義爲Layer,這個Layer其實和hwc2_layer_t是一樣的:

 

typedef uint64_t Layer;

都是Layer,但是在具體的代碼中要具體區分。

類型

Android中的 圖層按照有沒有Buffer分,有兩種類型的Layer:BufferLayer和ColorLayer。這也是Android實現中時,代碼實現時,採用的方式。

 

圖層Layer類型

  • BufferLayer 顧名思義,就是有Buffer的Layer,需要上層應用Producer去生產。

  • ColorLayer 可以繪製一種制定的顏色,和制定的透明度Alpha。它是取代之前的Dim Layer的,可以設置任何的顏色只,而不只是黑色。

按照數據格式分,可以分爲RGB Layer,YUV Layer。

  • RGB Layer
    Buffer是RGB格式,比較常見的就是UI界面的數據。

  • YUV Layer
    Buffer是YUV類型的,平常播放Video,Camera預覽等,都是YUV類型的。

屬性

Layer的屬性定義它與其他層的關係,和顯示屏的關係等。Layer包括的屬性類別如下:

  • 位置屬性
    定義層在其顯示設備上的顯示位置。包括層邊緣的位置及其相對於其他層的 Z-Order(指示該層在其他層之前還是之後)等信息。Layer中爲實現這一點,定義了除了z-order外,定義了很多個區域Region:

 

* frameworks/native/services/surfaceflinger/Layer.h

class Layer : public virtual RefBase {
    ... ...
public:
    ... ...
    // regions below are in window-manager space
    Region visibleRegion;
    Region coveredRegion;
    Region visibleNonTransparentRegion;
    Region surfaceDamageRegion;

Region中,是很多Rect的集合。簡言之,一個Layer的visibleRegion可能是幾個Rect的集合,其間的關係如下圖:

 

圖層Layer區域

SurfaceFlinger中定義的Region都是上層傳下來的,在WindowManager空間。而在HWC中,用下面的結構描述:

 

typedef struct hwc_frect {
    float left;
    float top;
    float right;
    float bottom;
} hwc_frect_t;

typedef struct hwc_rect {
    int left;
    int top;
    int right;
    int bottom;
} hwc_rect_t;

typedef struct hwc_region {
    size_t numRects;
    hwc_rect_t const* rects;
} hwc_region_t;

另外,在SurfaceFlinger中,還定義了一個重要的結構,Transform。Transform就是變換矩陣,它是一個3x3的矩陣。相關類的關係如下:

Transform類圖

 

每一個Layer的都有兩個狀態:mCurrentStatemDrawingState,mCurrentState是給SurfaceFlinger的前端準備數據,mDrawingState是給將數據給到合成;每個狀態有兩個Geometry的描述requestedactive,requested是上層請求的,active是當前正在用的;每個Geometry中,有一個Transform矩陣,一個Transform包含一個mat33的整列。

Transform中,其實包含兩部分,一部分是位置Postion,另外一部分纔是真正的2D的變換矩陣。通過下面兩個函數設置。

 

* frameworks/native/services/surfaceflinger/Transform.cpp

void Transform::set(float tx, float ty)
{
    mMatrix[2][0] = tx;
    mMatrix[2][1] = ty;
    mMatrix[2][2] = 1.0f;

    if (isZero(tx) && isZero(ty)) {
        mType &= ~TRANSLATE;
    } else {
        mType |= TRANSLATE;
    }
}

void Transform::set(float a, float b, float c, float d)
{
    mat33& M(mMatrix);
    M[0][0] = a;    M[1][0] = b;
    M[0][1] = c;    M[1][1] = d;
    M[0][2] = 0;    M[1][2] = 0;
    mType = UNKNOWN_TYPE;
}

這兩個函數對應Layer中的setPosition和setMatrix函數;這是上層WindowManager設置下來的。

  • 內容屬性。定義顯示內容如何呈現,顯示的內容也就是Buffer。
    Layer的顯示,除了前面說道的幾個區域描述,很有很多結構進一步的描述才能最終顯示出來。包括諸如剪裁(用來擴展內容的一部分以填充層的邊界)和轉換(用來顯示旋轉或翻轉的內容)等信息。HWCInfo結構體中 包括了一些這些信息:

 

* frameworks/native/services/surfaceflinger/Layer.h

    struct HWCInfo {
        HWCInfo()
              : hwc(nullptr),
                layer(nullptr),
                forceClientComposition(false),
                compositionType(HWC2::Composition::Invalid),
                clearClientTarget(false) {}

        HWComposer* hwc;
        HWC2::Layer* layer;
        bool forceClientComposition;
        HWC2::Composition compositionType;
        bool clearClientTarget;
        Rect displayFrame;
        FloatRect sourceCrop;
        HWComposerBufferCache bufferCache;
    };

怎麼理解這裏的sourceCropdisplayFrame
如果再加上可見區域visibleRegion呢?
再加上damageRegion呢?
還有Transform呢?

我們來個神圖,讓你一下子就能明白:

 

Android顯示區域間的關係

看圖說話:

  1. Layer區域和屏幕區域,就是Layer和屏幕本身的大小區域

  2. sourceCrop 剪切區域。
    sourceCrop是對Layer進行剪切的,值截取部分Layer的內容進行顯示;sourceCrop不超過Layer的大小,超過沒有意義。

  3. displayFrame 顯示區域。
    displayFrame表示Layer在屏幕上的顯示區域,具體說來,是sourceCrop區域在顯示屏上的顯示區域。displayFrame一般來說,小於屏幕的區域。而displayFrame可能比sourceCrop大,可能小,這都是正常的,只是需要做縮放,這就是合成時需要處理的。

  4. visibleRegion 可見區域。
    displayFrame 區域不一定都能看到的,如果存在上層Layer,那麼displayFrame區域可能部分或全部被蓋住,displayFrame沒有被蓋住的部分就是可見區域visibleRegion。

  5. damageRegion 受損區域,或者稱之爲更新區域。
    damageRegion表示Layer內容被破壞的區域,也就是說這部分區域的內容變了,所以這個屬性一般是和上一幀相比時纔有意義。這算是對合成的一種優化,重新合成時,我們只去合成damageRegion區域,其他的可見區域還是用的上一幀的數據。

  6. visibleNonTransparentRegion 可見非透明區域。
    透明區域transparentRegion是可見區域visibleRegion的一部分,只是這一部分透明的看到的是底層Layer的內容。在SurfaceFlinger的Layer中定義visibleNonTransparentRegion,表示可見而又不透明的部分。

  7. coveredRegion 被覆蓋的區域。
    表示Layer被TopLayer覆蓋的區域,一看圖就很好理解。從圖中,你可以簡單的認爲是displayFrame和TopLayer區域重合的部分。

注意, 這裏之所以說簡單的認爲,這是因爲HWC空間的區域大小是SurfaceFlinger空間的區域經過縮放,經過Transform旋轉,移動等後才得出的,要是混淆了就理解不對了。

  • 合成屬性。定義層應如何與其他層合成。包括混合模式和用於 Alpha 合成的全層 Alpha 值等信息。
    總的說來,合成分爲兩個大類:GPU合成,HWC合成。根據具體的情況,分爲下列幾類:

 

* hardware/libhardware/include/hardware/hwcomposer2.h

enum class Composition : int32_t {
    Invalid = HWC2_COMPOSITION_INVALID,
    Client = HWC2_COMPOSITION_CLIENT,
    Device = HWC2_COMPOSITION_DEVICE,
    SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
    Cursor = HWC2_COMPOSITION_CURSOR,
    Sideband = HWC2_COMPOSITION_SIDEBAND,
};
  1. Client 相對HWC2硬件合成的概念,主要是處理BufferLayer數據,用GPU處理。
  2. Device HWC2硬件設備,主要處理BufferLayer數據,用HWC處理
  3. SolidColor 固定顏色合成,主要處理ColorLayer數據,用HWC處理或GPU處理。
  4. Cursor 鼠標標識合成,主要處理鼠標等圖標,用HWC處理或GPU處理
  5. Sideband Sideband爲視頻的邊頻帶,一般需要需要硬件合成器作特殊處理,但是也可以用GPU處理。

在合成信息HWCInfo中,包含成的類型。通過Layer的setCompositionType方法進行指定:

 

void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc) {
    ... ...
    auto& hwcInfo = getBE().mHwcLayers[hwcId];
    auto& hwcLayer = hwcInfo.layer;
    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), to_string(type).c_str(),
          static_cast<int>(callIntoHwc));
    if (hwcInfo.compositionType != type) {
        ALOGV("    actually setting");
        hwcInfo.compositionType = type;
        if (callIntoHwc) {
            auto error = hwcLayer->setCompositionType(type);
            ... ...
        }
    }
}

callIntoHwc是否設置到HWC中,默認參數爲true。其實確定合成類型分3步,第一步,SurfaceFlinger制定合成類型,callIntoHwc這個時候callIntoHwc爲true,將類型制定給HWC。第二步,HWC根據實際情況,看看SurfaceFlinger制定的合成類型能不能執行,如果條件不滿足,做出修改;第三步,SurfaceFlinger根據HWC的修改情況,再做調整,最終確認合成類型,這個時候callIntoHwc參數設置爲false。

  • 優化屬性。提供一些非必須的參數,以供HWC進行合成的優化。包括層的可見區域以及層的哪個部分自上一幀以來已經更新等信息。也就是前面說到的visibleRegion,damageRegion等。

顯示屏Display

顯示屏Display是合成的另一個重要單元。系統可以具有多個顯示設備,並且在正常系統操作期間可以添加或刪除顯示設備。該添加/刪除可以應 HWC 設備的熱插拔請求,或者應客戶端的請求進行,這允許創建虛擬顯示設備,其內容會渲染到離屏緩衝區(而不是物理顯示設備)。

HWC中,SurfaceFlinger中創建的Layer,在合成開始時,將被指定到每個Display上,此後合成過程中,每個Display合成指定給自己的Layer。

參考前面我們大的類圖:
SurfaceFlinger前端,每個顯示屏,用DisplayDevice類描述,在後端顯示數據用DisplayData描述。而在HWC2的Client端,定義了Display類進行描述。對於HWC2服務端則用hwc2_display_t描述,hwc2_display_t只是一個序號,Vendor具體實現時,才具體的去管理Display的信息。

HWC2 提供相應函數來確定給定顯示屏的屬性,在不同配置(例如 4k 或 1080p 分辨率)和顏色模式(例如native顏色或真彩 sRGB)之間切換,以及打開、關閉顯示設備或將其切換到低功率模式(如果支持)。

HWC設備 composerDevice

一定要注意顯示屏,和合成設備的差別。HWC設備就一個,在頭文件中定義如下:

 

typedef struct hwc2_device {
    struct hw_device_t common;

    void (*getCapabilities)(struct hwc2_device* device, uint32_t* outCount,
            int32_t* /*hwc2_capability_t*/ outCapabilities);

    hwc2_function_pointer_t (*getFunction)(struct hwc2_device* device,
            int32_t /*hwc2_function_descriptor_t*/ descriptor);
} hwc2_device_t;

在HWC 的Client端,採用Device描述,底層採用hwc2_device_t描述。整個合成服務都是圍繞hwc2_device_t展開的。

除了層和顯示設備之外,HWC2 還提供對硬件垂直同步 (VSYNC) 信號的控制,以及對於客戶端的回調,用於通知它何時發生 vsync 事件。

接口指針

HWC2 頭文件中,HWC 接口函數由 lowerCamelCase 命名慣例 定義,但是這些函數名稱的字段並不實際存在於接口中。相反,幾乎每個函數都是通過使用 hwc2_device_t 提供的 getFunction 請求函數指針來進行加載。例如,函數 createLayer 是一個 HWC2_PFN_CREATE_LAYER 類型的函數指針,當枚舉值 HWC2_FUNCTION_CREATE_LAYER 傳遞到 getFunction 中時便會返回該指針。

接口指針定義在hwcomposer2.h中,基本上都是以HWC2_PFN*開始命名,接口比較多,這裏就不貼代碼了。而每個接口,都對應一個接口描述hwc2_function_descriptor_t。hwc2_function_descriptor_t定義如下,就是一個枚舉列表。

 

/* Function descriptors for use with getFunction */
typedef enum {
    HWC2_FUNCTION_INVALID = 0,
    HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES,
    HWC2_FUNCTION_CREATE_LAYER,
    HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY,
    HWC2_FUNCTION_DESTROY_LAYER,
    HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY,
    HWC2_FUNCTION_DUMP,
    HWC2_FUNCTION_GET_ACTIVE_CONFIG,
    HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES,
    HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT,
    HWC2_FUNCTION_GET_COLOR_MODES,
    HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE,
    HWC2_FUNCTION_GET_DISPLAY_CONFIGS,
    HWC2_FUNCTION_GET_DISPLAY_NAME,
    HWC2_FUNCTION_GET_DISPLAY_REQUESTS,
    HWC2_FUNCTION_GET_DISPLAY_TYPE,
    HWC2_FUNCTION_GET_DOZE_SUPPORT,
    HWC2_FUNCTION_GET_HDR_CAPABILITIES,
    HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT,
    HWC2_FUNCTION_GET_RELEASE_FENCES,
    HWC2_FUNCTION_PRESENT_DISPLAY,
    HWC2_FUNCTION_REGISTER_CALLBACK,
    HWC2_FUNCTION_SET_ACTIVE_CONFIG,
    HWC2_FUNCTION_SET_CLIENT_TARGET,
    HWC2_FUNCTION_SET_COLOR_MODE,
    HWC2_FUNCTION_SET_COLOR_TRANSFORM,
    HWC2_FUNCTION_SET_CURSOR_POSITION,
    HWC2_FUNCTION_SET_LAYER_BLEND_MODE,
    HWC2_FUNCTION_SET_LAYER_BUFFER,
    HWC2_FUNCTION_SET_LAYER_COLOR,
    HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE,
    HWC2_FUNCTION_SET_LAYER_DATASPACE,
    HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME,
    HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA,
    HWC2_FUNCTION_SET_LAYER_SIDEBAND_STREAM,
    HWC2_FUNCTION_SET_LAYER_SOURCE_CROP,
    HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE,
    HWC2_FUNCTION_SET_LAYER_TRANSFORM,
    HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION,
    HWC2_FUNCTION_SET_LAYER_Z_ORDER,
    HWC2_FUNCTION_SET_OUTPUT_BUFFER,
    HWC2_FUNCTION_SET_POWER_MODE,
    HWC2_FUNCTION_SET_VSYNC_ENABLED,
    HWC2_FUNCTION_VALIDATE_DISPLAY,
} hwc2_function_descriptor_t;

所以,Vendor的HWC2實現,基本都是這樣,僞代碼:

 

class VendorComposer2  : public hwc2_device_t {
    ... ...
    static void GetCapabilities(struct hwc2_device *device, uint32_t *outCount, int32_t *outCapabilities);
    static hwc2_function_pointer_t GetFunction(struct hwc2_device *device, int32_t descriptor);
    
        // 具體的接口實現
    static int32_t VendorAcceptDisplayChanges(hwc2_device_t *device, hwc2_display_t display);
    static int32_t VendorCreateLayer(hwc2_device_t *device, hwc2_display_t display,
                             hwc2_layer_t *out_layer_id);
    static int32_t VendorCreateVirtualDisplay(hwc2_device_t *device, uint32_t width, uint32_t height,
                                      int32_t *format, hwc2_display_t *out_display_id);
        ... ...
}

// GetFunction中
hwc2_function_pointer_t HWCSession::GetFunction(struct hwc2_device *device,
                                                int32_t int_descriptor) {
  auto descriptor = static_cast<HWC2::FunctionDescriptor>(int_descriptor);

  switch (descriptor) {
    case HWC2::FunctionDescriptor::AcceptDisplayChanges:
      return AsFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(VendorAcceptDisplayChanges);
    case HWC2::FunctionDescriptor::CreateLayer:
      return AsFP<HWC2_PFN_CREATE_LAYER>(VendorCreateLayer);
    case HWC2::FunctionDescriptor::CreateVirtualDisplay:
      return AsFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(Vendor:CreateVirtualDisplay);
    ... ...

句柄Handle

這個前面穿插將過了。Layer,Display和配置信息Config在HWC都是用各自的Handle表示的。

 

typedef uint32_t hwc2_config_t;
typedef uint64_t hwc2_display_t;
typedef uint64_t hwc2_layer_t;

Buffer也是用handle來描述native_handle_t

當 SurfaceFlinger 想要創建新層時,它會調用 createLayer 函數,然後返回一個 hwc2_layer_t 類型的句柄,。在此之後,SurfaceFlinger 每次想要修改該層的屬性時,都會將該 hwc2_layer_t 值以及進行修改所需的任何其他信息傳遞給相應的修改函數。hwc2_layer_t 類型句柄的大小足以容納一個指針或一個索引,並且 SurfaceFlinger 會將其視爲不透明,從而爲 HWC 實現人員提供最大的靈活性。

HWC合成服務

代碼位置:

 

hardware/interfaces/graphics/composer/2.1/default

這個HWC的的默認服務。SurfaceFlinger初始化時,可以通過屬性debug.sf.hwc_service_name來制定,默認爲default

 

* frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

std::string getHwcServiceName() {
    char value[PROPERTY_VALUE_MAX] = {};
    property_get("debug.sf.hwc_service_name", value, "default");
    ALOGI("Using HWComposer service: '%s'", value);
    return std::string(value);
}

在編譯時,manifest.xml中配置的也是default。

HWC服務分兩部分:

  • 可以執行程序 [email protected]
    其main函數如下,通過defaultPassthroughServiceImplementation函數註冊IComposer服務。

 

hardware/interfaces/graphics/composer/2.1/default/service.cpp

int main() {
    // the conventional HAL might start binder services
    android::ProcessState::initWithDriver("/dev/vndbinder");
    android::ProcessState::self()->setThreadPoolMaxThreadCount(4);
    android::ProcessState::self()->startThreadPool();

    // same as SF main thread
    struct sched_param param = {0};
    param.sched_priority = 2;
    if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK,
                &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO: %d", errno);
    }

    return defaultPassthroughServiceImplementation<IComposer>(4);
}

對應的rc文件如下:

 

service vendor.hwcomposer-2-1 /vendor/bin/hw/[email protected]
    class hal animation
    user system
    group graphics drmrpc
    capabilities SYS_NICE
    onrestart restart surfaceflinge
  • 實現庫 [email protected]
    hwc的執行程序中,註冊的IComposer,將調到對應的FETCH函數,FETCH函數實現及是so庫中。FETCH如下:

 

IComposer* HIDL_FETCH_IComposer(const char*)
{
    const hw_module_t* module = nullptr;
    int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &module);
    if (err) {
        ALOGI("falling back to FB HAL");
        err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    }
    if (err) {
        ALOGE("failed to get hwcomposer or fb module");
        return nullptr;
    }

    return new HwcHal(module);
}

FETCH函數中,才正在去加載Vendor的實現,通過統一的接口hw_get_module根據IDHWC_HARDWARE_MODULE_ID去加載。加載完成後,創建HAL描述類似HwcHal。

 

HwcHal::HwcHal(const hw_module_t* module)
    : mDevice(nullptr), mDispatch(), mMustValidateDisplay(true), mAdapter() {
    uint32_t majorVersion;
    if (module->id && strcmp(module->id, GRALLOC_HARDWARE_MODULE_ID) == 0) {
        majorVersion = initWithFb(module);
    } else {
        majorVersion = initWithHwc(module);
    }

    initCapabilities();
    if (majorVersion >= 2 && hasCapability(HWC2_CAPABILITY_PRESENT_FENCE_IS_NOT_RELIABLE)) {
        ALOGE("Present fence must be reliable from HWC2 on.");
        abort();
    }

    initDispatch();
}

如果是FrameBuffer驅動,通過initWithFb初始化。如果是HWC驅動,通過initWithHwc初始化。我們需要的是HWC2的接口,如果不是HWC2的HAl實現,那麼需要做適配。

FrameBuffer驅動,採用HWC2OnFbAdapter進行適配:

 

uint32_t HwcHal::initWithFb(const hw_module_t* module)
{
    framebuffer_device_t* fb_device;
    int error = framebuffer_open(module, &fb_device);
    if (error != 0) {
        ALOGE("Failed to open FB device (%s), aborting", strerror(-error));
        abort();
    }

    mFbAdapter = std::make_unique<HWC2OnFbAdapter>(fb_device);
    mDevice = mFbAdapter.get();

    return 0;
}

HWC1.x通過HWC2On1Adapter進行適配:

 

uint32_t HwcHal::initWithHwc(const hw_module_t* module)
{
    // Determine what kind of module is available (HWC2 vs HWC1.X).
    hw_device_t* device = nullptr;
    int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);
    ... ...
    uint32_t majorVersion = (device->version >> 24) & 0xF;

    // If we don't have a HWC2, we need to wrap whatever we have in an adapter.
    if (majorVersion != 2) {
        uint32_t minorVersion = device->version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
        minorVersion = (minorVersion >> 16) & 0xF;
        ALOGI("Found HWC implementation v%d.%d", majorVersion, minorVersion);
        if (minorVersion < 1) {
            ALOGE("Cannot adapt to HWC version %d.%d. Minimum supported is 1.1",
                  majorVersion, minorVersion);
            abort();
        }
        mAdapter = std::make_unique<HWC2On1Adapter>(
                reinterpret_cast<hwc_composer_device_1*>(device));

        // Place the adapter in front of the device module.
        mDevice = mAdapter.get();
    } else {
        mDevice = reinterpret_cast<hwc2_device_t*>(device);
    }

    return majorVersion;
}

這兩種適配的實現代碼如下:

 

frameworks/native/libs
├── hwc2onfbadapter
└── hwc2on1adapter

分別打包成兩個動態so庫,Adapter中再調具體的Vendor實現。

初始化時,第一步先獲取Vendor實現的處理能力:

 

void HwcHal::initCapabilities()
{
    uint32_t count = 0;
    mDevice->getCapabilities(mDevice, &count, nullptr);

    std::vector<int32_t> caps(count);
    mDevice->getCapabilities(mDevice, &count, caps.data());
    caps.resize(count);

    mCapabilities.reserve(count);
    for (auto cap : caps) {
        mCapabilities.insert(static_cast<hwc2_capability_t>(cap));
    }
}

第二步,初始化,HWC的接口函數:

 

void HwcHal::initDispatch()
{
    initDispatch(HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES,
            &mDispatch.acceptDisplayChanges);
    initDispatch(HWC2_FUNCTION_CREATE_LAYER, &mDispatch.createLayer);
    initDispatch(HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY,
            &mDispatch.createVirtualDisplay);
    ... ...

根據ID,同過Vendor實現的getFunction函數,去獲取Vendor對應的函數地址。

 

template<typename T>
void HwcHal::initDispatch(hwc2_function_descriptor_t desc, T* outPfn)
{
    auto pfn = mDevice->getFunction(mDevice, desc);
    if (!pfn) {
        LOG_ALWAYS_FATAL("failed to get hwcomposer2 function %d", desc);
    }

    *outPfn = reinterpret_cast<T>(pfn);
}

Client和Server的通信

SurfaceFlinger和HWC服務之間,很多函數,並沒有直接的調用,而是通過Buffer的讀寫來實現調用和參數的傳遞的。所以,Client端和Server端通信,基本通過以下相關的途徑:

  • 通過IComposerClient.hal接口
  • 通過IComposer.hal接口
  • 通過command Buffer

Server端回調Client端,通過IComposerCallback.hal接口。

hal接口的方式,比較好理解,其本質就是Binder。那麼問題來了,既然有hal的接口,爲什麼又加了一個command Buffer的方式呢?其實這是爲了解決Binder通信慢的問題。

我們先來看看IComposerClient.hal接口

IComposerClient.hal 接口

IComposerClient.hal的接口如下:

 

* hardware/interfaces/graphics/composer/2.1/IComposerClient.hal

    registerCallback(IComposerCallback callback);

    getMaxVirtualDisplayCount() generates (uint32_t count);

    createVirtualDisplay(uint32_t width,
                         uint32_t height,
                         PixelFormat formatHint,
                         uint32_t outputBufferSlotCount)
              generates (Error error,
                         Display display,
                         PixelFormat format);

    destroyVirtualDisplay(Display display) generates (Error error);

    createLayer(Display display,
                uint32_t bufferSlotCount)
     generates (Error error,
                Layer layer);

    destroyLayer(Display display, Layer layer) generates (Error error);

    getActiveConfig(Display display) generates (Error error, Config config);

    getClientTargetSupport(Display display,
                           uint32_t width,
                           uint32_t height,
                           PixelFormat format,
                           Dataspace dataspace)
                generates (Error error);

    getColorModes(Display display)
       generates (Error error,
                  vec<ColorMode> modes);

    getDisplayAttribute(Display display,
                        Config config,
                        Attribute attribute)
             generates (Error error,
                        int32_t value);

    getDisplayConfigs(Display display)
           generates (Error error,
                      vec<Config> configs);

    getDisplayName(Display display) generates (Error error, string name);

    getDisplayType(Display display) generates (Error error, DisplayType type);

    getDozeSupport(Display display) generates (Error error, bool support);

    getHdrCapabilities(Display display)
            generates (Error error,
                       vec<Hdr> types,
                       float maxLuminance,
                       float maxAverageLuminance,
                       float minLuminance);

    setClientTargetSlotCount(Display display,
                             uint32_t clientTargetSlotCount)
                  generates (Error error);

    setActiveConfig(Display display, Config config) generates (Error error);

    setColorMode(Display display, ColorMode mode) generates (Error error);

    setPowerMode(Display display, PowerMode mode) generates (Error error);

    setVsyncEnabled(Display display, Vsync enabled) generates (Error error);

    setInputCommandQueue(fmq_sync<uint32_t> descriptor)
              generates (Error error);

    getOutputCommandQueue()
              generates (Error error,
                         fmq_sync<uint32_t> descriptor);

    executeCommands(uint32_t inLength,
                    vec<handle> inHandles)
         generates (Error error,
                    bool outQueueChanged,
                    uint32_t outLength,
                    vec<handle> outHandles);

IComposerClient.hal的接口函數比較多,這裏提供的接口,都是一些實時的接口,也就是Client需要Server立即響應的接口。

IComposer.hal接口

IComposer.hal就3個接口函數,createClient主要的是這裏的createClient函數,創建一個ComposerClient,通過ComposerClient,就可以用上面的IComposerClient.hal接口。

 

* hardware/interfaces/graphics/composer/2.1/IComposer.hal

    getCapabilities() generates (vec<Capability> capabilities);

    dumpDebugInfo() generates (string debugInfo);

    createClient() generates (Error error, IComposerClient client);

IComposer.hal接口也是實時接口。

IComposerCallback.hal接口

IComposerCallback.hal接口是Server回調給Client端的,主要是下面3個接口:

 

    onHotplug(Display display, Connection connected);

    oneway onRefresh(Display display);

    oneway onVsync(Display display, int64_t timestamp);

三個接口分別上報熱插拔,刷新請求,和Vsync事件。另外,IComposerCallback接口需要通過IComposerClient.hal的registerCallback函數進行註冊。

command Buffer

相比前面的hal接口,這裏的Buffer方式的調用都不是實時的,先將命令寫到Buffer中,最後再一次性的調用到Server。

相關的定義在頭文件IComposerCommandBuffer.h中,定義了一個CommandWriterBase和一個CommandReaderBase

 

* hardware/interfaces/graphics/composer/2.1/default/IComposerCommandBuffer.h

class CommandWriterBase {
    ... ...
    uint32_t mDataMaxSize;
    std::unique_ptr<uint32_t[]> mData;

    uint32_t mDataWritten;
    // end offset of the current command
    uint32_t mCommandEnd;

    std::vector<hidl_handle> mDataHandles;
    std::vector<native_handle_t *> mTemporaryHandles;

    std::unique_ptr<CommandQueueType> mQueue;
};

class CommandReaderBase {
    ... ...
    std::unique_ptr<CommandQueueType> mQueue;
    uint32_t mDataMaxSize;
    std::unique_ptr<uint32_t[]> mData;

    uint32_t mDataSize;
    uint32_t mDataRead;

    // begin/end offsets of the current command
    uint32_t mCommandBegin;
    uint32_t mCommandEnd;

    hidl_vec<hidl_handle> mDataHandles;
};

都是用一個uint32_t的數組來保存數據mData。Client端和Server端基於兩個Base類,又繼承Base,重寫各自的Reader和Writer。

相關的類圖如下:

 

命令讀寫器

相關的command命令,定義在IComposerClient.hal中

 

* hardware/interfaces/graphics/composer/2.1/IComposerClient.hal

enum Command : int32_t {
        LENGTH_MASK                        = 0xffff,
        OPCODE_SHIFT                       = 16,
        OPCODE_MASK                        = 0xffff << OPCODE_SHIFT,

        /** special commands */
        SELECT_DISPLAY                     = 0x000 << OPCODE_SHIFT,
        SELECT_LAYER                       = 0x001 << OPCODE_SHIFT,
        ... ...

我們以setZOrder函數爲例,對應的command爲SET_LAYER_Z_ORDER

 

SET_LAYER_Z_ORDER                  = 0x40a << OPCODE_SHIFT,

對應的函數爲:

 

Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z)
{
    mWriter.selectDisplay(display);
    mWriter.selectLayer(layer);
    mWriter.setLayerZOrder(z);
    return Error::NONE;
}

setZOrder時:

1.首先指定Display

 

* hardware/interfaces/graphics/composer/2.1/default/IComposerCommandBuffer.h

    static constexpr uint16_t kSelectDisplayLength = 2;
    void selectDisplay(Display display)
    {
        beginCommand(IComposerClient::Command::SELECT_DISPLAY,
                kSelectDisplayLength);
        write64(display);
        endCommand();
    }

指定Display也是通過一個命令來完成,傳遞一個命令具體分爲3步:

  • beginCommand
    beginCommand先寫命令值,SELECT_DISPLAY

 

    void beginCommand(IComposerClient::Command command, uint16_t length)
    {
        if (mCommandEnd) {
            LOG_FATAL("endCommand was not called before command 0x%x",
                    command);
        }

        growData(1 + length);
        write(static_cast<uint32_t>(command) | length);

        mCommandEnd = mDataWritten + length;
    }

beginCommand時,先增加Buffer的大小,Buffer採用一個uint32_t類型的數組。

 

std::unique_ptr<uint32_t[]> mData;

再將command和長度或後,寫入Buffer。最後記錄,Buffer應該結束的位置mCommandEnd。這裏的+1是爲了寫command命令,是command命令的長度。

  • 寫具體的值
    display是64bit的,所以直接用write64的接口;將被拆分爲兩個32位,用32位的接口write寫入Buffer。寫入後,mDataWritten相應的增加。

 

    void write(uint32_t val)
    {
        mData[mDataWritten++] = val;
    }
  • endCommand
    函數如下:

 

    void endCommand()
    {
        if (!mCommandEnd) {
            LOG_FATAL("beginCommand was not called");
        } else if (mDataWritten > mCommandEnd) {
            LOG_FATAL("too much data written");
            mDataWritten = mCommandEnd;
        } else if (mDataWritten < mCommandEnd) {
            LOG_FATAL("too little data written");
            while (mDataWritten < mCommandEnd) {
                write(0);
            }
        }

        mCommandEnd = 0;
    }

endCommand中主要是看我們寫的數據對不對,如果寫的太多,超過mCommandEnd的截掉。如果寫的太少,補0。

2.指定Layer
通過selectLayer函數指定layer;selectLayer函數和前面的selectDisplay類似:

 

    static constexpr uint16_t kSelectLayerLength = 2;
    void selectLayer(Layer layer)
    {
        beginCommand(IComposerClient::Command::SELECT_LAYER,
                kSelectLayerLength);
        write64(layer);
        endCommand();
    }

3.將要設置的數據寫如Buffer

 

    static constexpr uint16_t kSetLayerZOrderLength = 1;
    void setLayerZOrder(uint32_t z)
    {
        beginCommand(IComposerClient::Command::SET_LAYER_Z_ORDER,
                kSetLayerZOrderLength);
        write(z);
        endCommand();
    }

設置 z-order 時,用的SET_LAYER_Z_ORDER命令。z-order是32bit的類型,這裏直接用的write函數。

到此,z-order寫到Buffer mData中。注意,只是寫到了mData中,還沒有傳到HWC的服務端呢。

4.執行命令
Buffer中的數據什麼時候才傳到HWC的服務端呢? 答案是 executeCommands的時候。execute 基本就是validate和present處理的時候會先調,execute將Buffer命令真正是傳到Server進行解析,調相應的接口。execute主要是通過IComposerClient.hal的實時接口來完成。

execute函數如下:

 

Error Composer::execute()
{
    // 準備command隊列
    bool queueChanged = false;
    uint32_t commandLength = 0;
    hidl_vec<hidl_handle> commandHandles;
    if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
        mWriter.reset();
        return Error::NO_RESOURCES;
    }

    // set up new input command queue if necessary
    if (queueChanged) {
        auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
        auto error = unwrapRet(ret);
        if (error != Error::NONE) {
            mWriter.reset();
            return error;
        }
    }

    if (commandLength == 0) {
        mWriter.reset();
        return Error::NONE;
    }

    Error error = kDefaultError;
    auto ret = mClient->executeCommands(commandLength, commandHandles,
            [&](const auto& tmpError, const auto& tmpOutChanged,
                const auto& tmpOutLength, const auto& tmpOutHandles)
            {
                ... ...
                if (error == Error::NONE && tmpOutChanged) {
                    error = kDefaultError;
                    mClient->getOutputCommandQueue(
                            [&](const auto& tmpError,
                                const auto& tmpDescriptor)
                            {
                                ... ...

                                mReader.setMQDescriptor(tmpDescriptor);
                            });
                }

                ... ...

                if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
                   ... ...
            });
    ... ...

    mWriter.reset();

    return error;
}

execute主要做了下面幾件事:

  • 準備命令隊列commandQueue
    通過Writer的writeQueue來實現,其作用就是將前面我們已經寫到Buffer中的command及數據,寫到命令隊列mQueue中。代碼如下:

 

    bool writeQueue(bool* outQueueChanged, uint32_t* outCommandLength,
            hidl_vec<hidl_handle>* outCommandHandles)
    {
        ... ...
        } else {
            auto newQueue = std::make_unique<CommandQueueType>(mDataMaxSize);
            if (!newQueue->isValid() ||
                    !newQueue->write(mData.get(), mDataWritten)) {
                ALOGE("failed to prepare a new message queue ");
                return false;
            }

            mQueue = std::move(newQueue);
            *outQueueChanged = true;
        }

        *outCommandLength = mDataWritten;
        outCommandHandles->setToExternal(
                const_cast<hidl_handle*>(mDataHandles.data()),
                mDataHandles.size());

        return true;
    }
  • 設置新的命令隊列
    setInputCommandQueue傳遞的是命令隊列的文件描述符,Server端Reader根據隊列的文件描述符去找對應的隊列。

 

Return<Error> ComposerClient::setInputCommandQueue(
        const MQDescriptorSync<uint32_t>& descriptor)
{
    std::lock_guard<std::mutex> lock(mCommandMutex);
    return mReader->setMQDescriptor(descriptor) ?
        Error::NONE : Error::NO_RESOURCES;
}

setMQDescriptor時Reader會根據描述符創建對應的命令隊形。

  • 執行命令隊列
    executeCommands在server端的實現爲:

 

Return<void> ComposerClient::executeCommands(uint32_t inLength,
        const hidl_vec<hidl_handle>& inHandles,
        executeCommands_cb hidl_cb)
{
    std::lock_guard<std::mutex> lock(mCommandMutex);

    bool outChanged = false;
    uint32_t outLength = 0;
    hidl_vec<hidl_handle> outHandles;

    if (!mReader->readQueue(inLength, inHandles)) {
        hidl_cb(Error::BAD_PARAMETER, outChanged, outLength, outHandles);
        return Void();
    }

    Error err = mReader->parse();
    if (err == Error::NONE &&
            !mWriter.writeQueue(&outChanged, &outLength, &outHandles)) {
        err = Error::NO_RESOURCES;
    }

    hidl_cb(err, outChanged, outLength, outHandles);

    mReader->reset();
    mWriter.reset();

    return Void();
}

server端的Reader讀取命令隊列,將命令,數據等從mQueue中又讀到Reader的Buffer mData中。讀到Mdata中後,再解析parse。

 

Error ComposerClient::CommandReader::parse()
{
    IComposerClient::Command command;
    uint16_t length = 0;

    while (!isEmpty()) {
        if (!beginCommand(&command, &length)) {
            break;
        }

        bool parsed = parseCommand(command, length);
        endCommand();

        if (!parsed) {
            ALOGE("failed to parse command 0x%x, length %" PRIu16,
                    command, length);
            break;
        }
    }

    return (isEmpty()) ? Error::NONE : Error::BAD_PARAMETER;
}

解析命令,也分3步:
beginCommand 讀取命令,看看是什麼命令;
parseCommand 解析命令,根據命令,解析具體的數據。比如我們設置z-order的命令處理如下:

 

bool ComposerClient::CommandReader::parseSetLayerZOrder(uint16_t length)
{
    if (length != CommandWriterBase::kSetLayerZOrderLength) {
        return false;
    }

    auto err = mHal.setLayerZOrder(mDisplay, mLayer, read());
    if (err != Error::NONE) {
        mWriter.setError(getCommandLoc(), err);
    }

    return true;
}

parseCommand時數據才真正傳遞到Server中,生效。z-order通過mHal的setLayerZOrder設置到Vendor的HAL實現中。

endCommand 表示數據讀取完,記錄讀取的位置。

回到啊Client端execute函數。Client端的Reader也會讀取返回值,

HWC2 中Fence的更改

HWC 2.0 中同步柵欄的含義相對於以前版本的 HAL 已有很大的改變。

在 HWC v1.x 中,釋放Fence和退出Fence是推測性的。在幀 N 中檢索到的Buffer的釋放Fence或顯示設備的退出Fence不會先於在幀 N + 1 中檢索到的Fence變爲觸發狀態。換句話說,該Fence的含義是“不再需要您爲幀 N 提供的Buffer內容”。這是推測性的,因爲在理論上,SurfaceFlinger 在幀 N 之後的一段不確定的時間內可能無法再次運行,這將使得這些柵欄在該時間段內不會變爲觸發狀態。

在 HWC 2.0 中,釋放Fence和退出Fence是非推測性的。在幀 N 中檢索到的釋放Fence或退出Fence,將在相關Buffer的內容替換幀 N - 1 中緩衝區的內容後立即變爲觸發狀態,或者換句話說,該Fence的含義是“您爲幀 N 提供的緩衝區內容現在已經替代以前的內容”。這是非推測性的,因爲在硬件呈現此幀的內容之後,該柵欄應該在 presentDisplay 被調用後立即變爲觸發狀態。

小結

這裏主要是總結性的介紹一下HWC2,很多流程,稍後我們在代碼中具體來分析。



作者:夕月風
鏈接:https://www.jianshu.com/p/824a9ddf68b9
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

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