Android P 圖形顯示系統(tǒng)(一)硬件合成HWC2

[TOC]

硬件合成HWC2

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

SurfaceFlinger是一個系統(tǒng)服務,其作用是接受來自多個源的Buffer數(shù)據(jù)忿族,對它們進行合成往枷,然后發(fā)送到顯示設備進行顯示。在之前的Android版本中贰逾,顯示基本都是基于硬件的FrameBuffer來實現(xiàn)的悬荣,例如/dev/graphics/fb0,但是在后來的版本中疙剑,實現(xiàn)可以多樣化了氯迂,比如高通采用SDM,其他有些平臺采用ADF言缤,DRM等嚼蚀。

SurfaceFlinger和HWC的相關配合,實現(xiàn)Android系統(tǒng)的合成與顯示~

SurfaceFlinger概述

大多數(shù)應用通常在屏幕上有三個層:屏幕頂部的狀態(tài)欄管挟、底部或側面的導航欄以及應用的界面轿曙。有些應用會擁有更多或更少的層(例如,默認主屏幕應用有一個單獨的壁紙層僻孝,而全屏游戲可能會隱藏狀態(tài)欄)导帝。每個層都可以單獨更新。狀態(tài)欄和導航欄由系統(tǒng)進程渲染穿铆,而應用層由應用渲染您单,兩者之間不進行協(xié)調(diào)。

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


Android顯示系統(tǒng)

從上圖虐秦,我們可以看出,在長按Power鍵讯檐,彈出關機對話框時羡疗,有4層Layer染服,可以立即為有4個窗口别洪。4個窗口經(jīng)過SurfaceFlinger進行合成后,再送到顯示器進行顯示柳刮。

我們來看看SurfaceFlinger的類定義:

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

SurfaceFlinger繼承BnSurfaceComposer挖垛,實現(xiàn)ISurfaceComposer接口。實現(xiàn)ComposerCallback秉颗。PriorityDumper是一個輔助類痢毒,主要提供SurfaceFlinger的信息dump,并提供信息的分離和格式設置蚕甥。

  • ** ISurfaceComposer接口實現(xiàn)**
    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中都有對應的方法實現(xiàn)。Client端通過Binder調(diào)到SurfaceFlinger中菇怀。前面我們已經(jīng)說過上層怎么獲取Display的信息凭舶,其實現(xiàn)就是SurfaceFlinger中的getDisplayConfigs函數(shù)晌块。其他的類似,根據(jù)上面Binder的command去找對應的實現(xiàn)帅霜。

  • ComposerCallback接口實現(xiàn)
    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接口的實現(xiàn)。

  • onHotplugReceived
    熱插拔事件的回調(diào)搂根,顯示屏幕連接或斷開時回調(diào)珍促。Surfaceflinger中實現(xiàn)的方法為SurfaceFlinger::onHotplugReceived。這塊邏輯剩愧,我們稍后介紹踢星。

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

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將觸發(fā)一次刷新五督,重新進行合成顯示藏否。

重新繪制,說明底層配置充包,參數(shù)等有變動副签,SurfaceFlinger前面給的數(shù)據(jù)不能用,得重新根據(jù)變動后的配置進行進行合成基矮,給適合當前配置的顯示數(shù)據(jù)淆储。

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

需要注意的是本砰,這里收到的熱插拔和Vsync回調(diào),又將被封裝為事件的形式再通知出去钢悲,這是回話点额,后續(xù)介紹。

  • 顯示周期Vsync

設備顯示會按一定速率刷新莺琳,在手機和平板電腦上通常為每秒 60 幀还棱。如果顯示內(nèi)容在刷新期間更新,則會出現(xiàn)撕裂現(xiàn)象惭等;因此珍手,請務必只在周期之間更新內(nèi)容。在可以安全更新內(nèi)容時,系統(tǒng)便會收到來自顯示設備的信號琳要。由于歷史原因料扰,我們將該信號稱為 VSYNC 信號。

刷新率可能會隨時間而變化焙蹭,例如晒杈,一些移動設備的刷新率范圍在 58 fps 到 62 fps 之間,具體要視當前條件而定孔厉。對于連接了 HDMI 的電視拯钻,刷新率在理論上可以下降到 24 Hz 或 48 Hz,以便與視頻相匹配撰豺。由于每個刷新周期只能更新屏幕一次粪般,因此以 200 fps 的刷新率為顯示設備提交緩沖區(qū)只是在做無用功,因為大多數(shù)幀永遠不會被看到污桦。SurfaceFlinger 不會在應用提交緩沖區(qū)時執(zhí)行操作亩歹,而是在顯示設備準備好接收新的緩沖區(qū)時才會喚醒。

當 VSYNC 信號到達時凡橱,SurfaceFlinger 會遍歷它的層列表小作,以尋找新的緩沖區(qū)。如果找到新的緩沖區(qū)稼钩,它會獲取該緩沖區(qū)顾稀;否則,它會繼續(xù)使用以前獲取的緩沖區(qū)坝撑。SurfaceFlinger 總是需要可顯示的內(nèi)容静秆,因此它會保留一個緩沖區(qū)。如果在某個層上沒有提交緩沖區(qū)巡李,則該層會被忽略抚笔。

  • 合成方式

目前SurfaceFlinger中支持兩種合成方式,一種是Device合成侨拦,一種是Client合成殊橙。SurfaceFlinger 在收集可見層的所有緩沖區(qū)之后,便會詢問 Hardware Composer 應如何進行合成阳谍。

  • Client合成
    Client合成方式是相對與硬件合成來說的蛀柴,其合成方式是螃概,將各個Layer的內(nèi)容用GPU渲染到暫存緩沖區(qū)中矫夯,最后將暫存緩沖區(qū)傳送到顯示硬件。這個暫存緩沖區(qū)吊洼,我們稱為FBTarget训貌,每個Display設備有各自的FBTarget。Client合成,之前稱為GLES合成递沪,我們也可以稱之為GPU合成豺鼻。Client合成,采用RenderEngine進行合成款慨。

  • Device合成
    就是用專門的硬件合成器進行合成HWComposer儒飒,所以硬件合成的能力就取決于硬件的實現(xiàn)。其合成方式是將各個Layer的數(shù)據(jù)全部傳給顯示硬件檩奠,并告知它從不同的緩沖區(qū)讀取屏幕不同部分的數(shù)據(jù)桩了。HWComposer是Devicehec的抽象。

所以埠戳,整個顯示系統(tǒng)的數(shù)據(jù)流如下圖所示井誉,此圖來源于Androd 官網(wǎng):


Android顯示系統(tǒng)數(shù)據(jù)流圖

GPU合成后數(shù)據(jù),作為一個特殊的Layer整胃,傳給顯示硬件颗圣。

  • dump信息
    dump信息是很好的一個調(diào)試手段,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就是后端除嘹,現(xiàn)在的SurfaceFlinger充當前端的角色写半。后端SurfaceFlingerBE主要就是和底層合成打交道,前端和上層進行交互尉咕。在后續(xù)的版本中叠蝇,更多的邏輯會被移到后端中。

  • 消息隊列和主線程
    和應用進程類似年缎,SurfaceFlinger也有一個主線程悔捶,SurfaceFlinger的主線程主要進行顯示數(shù)據(jù)的處理,也就是合成单芜。SurfaceFlinger是一個服務蜕该,將會響應上層的很多請求,各個進程的請求都在SurfaceFlinger的各個Binder線程中洲鸠,如果線程很耗時堂淡,那么應用端就會被block馋缅,顯示也會被block。主線程就是將他們分離開來绢淀,各干各的事萤悴。

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


SurfaceFlinger相關類圖

其他的都好理解,有兩個地方需要注意:
1.SurfaceFlinger有兩個狀態(tài)费薄,Layer也有兩個狀態(tài)内狗,一個mCurrentState,一個mDrawingState义锥。注意這里State類定義是不一樣的柳沙。
2.兩個EventThread,一個是給SurfaceFlinger本身用拌倍,一個是為了給應用分發(fā)事件的赂鲤。

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分發(fā)出來的数初,VSyncSources定期會和底層的Vsync進行同步,確保和底層屏幕新的步調(diào)一致梗顺。

HWC2 概述

Android 7.0 包含新版本的 HWC (HWC2)泡孩,Android需要自行配置存淫,到Android 8.0介时,HWC2正式開啟,且版本升級為2.1版本鹰服。HWC2是 SurfaceFlinger 用來與專門的窗口合成硬件進行通信变屁。SurfaceFlinger 包含使用 3D 圖形處理器 (GPU) 執(zhí)行窗口合成任務的備用路徑眼俊,但由于以下幾個原因,此路徑并不理想:

  • 通常粟关,GPU 未針對此用例進行過優(yōu)化疮胖,因此能耗可能要大于執(zhí)行合成所需的能耗。
  • 每次 SurfaceFlinger 使用 GPU 進行合成時闷板,應用都無法使用處理器進行自我渲染澎灸,因此應盡可能使用專門的硬件而不是 GPU 進行合成。

下面是GPU和HWC兩種方式的優(yōu)劣對比:

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

所以性昭,HWC的設計最好遵循一些基本的規(guī)則~

HWC 常規(guī)準則

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

  • HWC 應至少支持 4 個疊加層(狀態(tài)欄创泄、系統(tǒng)欄艺玲、應用和壁紙/背景)括蝠。
  • 層可以大于屏幕,因此 HWC 應能處理大于顯示屏的層(例如壁紙)饭聚。
  • 應同時支持預乘每像素 Alpha 混合和每平面 Alpha 混合忌警。
  • HWC 應能處理 GPU、相機和視頻解碼器生成的相同緩沖區(qū)秒梳,因此支持以下某些屬性很有幫助:
    • RGBA 打包順序
    • YUV 格式
    • Tiling, swizzling和步幅屬性
  • 為了支持受保護的內(nèi)容法绵,必須提供受保護視頻播放的硬件路徑。

Tiling酪碘,翻譯過來就沒有原文的意味了朋譬,說白了,就是將image進行切割兴垦,切成MxN的小塊徙赢,最后用的時候,再將這些小塊拼接起來探越,就像鋪瓷磚一樣狡赐。

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

HWC專注于優(yōu)化,智能地選擇要發(fā)送到疊加硬件的 Surface卷玉,以最大限度減輕 GPU 的負載哥倔。另一種優(yōu)化是檢測屏幕是否正在更新;如果不是揍庄,則將合成委托給 OpenGL 而不是 HWC咆蒿,以節(jié)省電量。當屏幕再次更新時蚂子,繼續(xù)將合成分載到 HWC沃测。

為常見用例做準備,如:

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

HWC2 框架

從Android 8.0開始的Treble項目,對Android的架構做了重大的調(diào)整别渔,讓制造商以更低的成本更輕松附迷、更快速地將設備更新到新版 Android 系統(tǒng)惧互。這就對 HAL 層有了很大的調(diào)整,利用提供給Vendor的接口喇伯,將Vendor的實現(xiàn)和Android上層分離開來喊儡。

這樣的架構讓HWC的架構也變的復雜,HWC屬于Binderized的HAL類型稻据。Binderized類型的HAL艾猜,將上層Androd和底層HAL分別采用兩個不用的進程實現(xiàn),中間采用Binder進行通信捻悯,為了和前面的Binder進行區(qū)別匆赃,這里采用HwBinder。

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


HWC2實現(xiàn)
  • Client端
    Client也就是SurfaceFlinger姓言,不過SurfaceFlinger采用前后端的設計瞬项,以后和HWC相關的邏輯應該都會放到SurfaceFlinger后端也就是SurfaceFlingerBE中。代碼位置:
frameworks/native/services/surfaceflinger
  • HWC2 Client端
    這一部分屬于SurfaceFlinger進程事期,其直接通過Binder通信滥壕,和HWC2的HAL Server交互。這部分的代碼也在SurfaceFlinger進程中兽泣,但是采用Hwc2的命名空間绎橘。

  • HWC Server端
    這一部分還是屬于Android的系統(tǒng),這里將建立一個進程唠倦,實現(xiàn)HWC的服務端称鳞,Server端再調(diào)底層Vendor的具體實現(xiàn)。并且稠鼻,對于底層合成的實現(xiàn)不同冈止,這里會做一些適配,適配HWC1.x候齿,和FrameBuffer的實現(xiàn)熙暴。這部分包含三部分:接口,實現(xiàn)和服務慌盯,以動態(tài)庫的形式存在:

android.hardware.graphics.composer@2.1.so
android.hardware.graphics.composer@2.1-impl.so
android.hardware.graphics.composer@2.1-service.so

代碼位置:

hardware/interfaces/graphics/composer/2.1/default
  • HWC Vendor的實現(xiàn)
    這部分是HWC的具體實現(xiàn)周霉,這部分的實現(xiàn)由硬件廠商完成。比如高通平臺亚皂,代碼位置一般為:
hardware/qcom/display

需要注意的是俱箱,HWC必須采用Binderized HAL模式,但是灭必,并沒有要求一定要實現(xiàn)HWC2的HAL版本狞谱。HWC2的實現(xiàn)需要配置乃摹,以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 數(shù)據(jù)結構

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

hardware/libhardware/include/hardware
    ├── hwcomposer2.h
    ├── hwcomposer_defs.h
    └── hwcomposer.h
hardware/interfaces/graphics/composer/2.1/types.hal

另外一些共用的數(shù)據(jù)定義是HAL的接口中:

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

當然肪康,SurfaceFlinger中有很多相關的數(shù)據(jù)結構:

frameworks/native/services/surfaceflinger

圖層Layer

圖層(Layer)是合成的最重要單元荚恶;每個圖層都有一組屬性撩穿,用于定義它與其他層的交互方式。Layer在每一層中的代碼的實現(xiàn)不一樣谒撼,基本上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中實現(xiàn)時廓潜,定義為hwc2_layer_t抵皱,這是在頭文件hwcomposer2.h中定義的

typedef uint64_t hwc2_layer_t;

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

typedef uint64_t Layer;

都是Layer辩蛋,但是在具體的代碼中要具體區(qū)分呻畸。

類型

Android中的 圖層按照有沒有Buffer分,有兩種類型的Layer:BufferLayer和ColorLayer悼院。這也是Android實現(xiàn)中時伤为,代碼實現(xiàn)時,采用的方式据途。


圖層Layer類型
  • BufferLayer 顧名思義绞愚,就是有Buffer的Layer,需要上層應用Producer去生產(chǎn)颖医。

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

按照數(shù)據(jù)格式分佛致,可以分為RGB Layer贮缕,YUV Layer。

  • RGB Layer
    Buffer是RGB格式晌杰,比較常見的就是UI界面的數(shù)據(jù)跷睦。

  • YUV Layer
    Buffer是YUV類型的,平常播放Video肋演,Camera預覽等抑诸,都是YUV類型的烂琴。

屬性

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

  • 位置屬性奸绷。
    定義層在其顯示設備上的顯示位置。包括層邊緣的位置及其相對于其他層的 Z-Order(指示該層在其他層之前還是之后)等信息层玲。Layer中為實現(xiàn)這一點号醉,定義了除了z-order外,定義了很多個區(qū)域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區(qū)域

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的都有兩個狀態(tài):mCurrentStatemDrawingState躺孝,mCurrentState是給SurfaceFlinger的前端準備數(shù)據(jù),mDrawingState是給將數(shù)據(jù)給到合成趟庄;每個狀態(tài)有兩個Geometry的描述requestedactive括细,requested是上層請求的,active是當前正在用的戚啥;每個Geometry中奋单,有一個Transform矩陣,一個Transform包含一個mat33的整列猫十。

Transform中览濒,其實包含兩部分,一部分是位置Postion拖云,另外一部分才是真正的2D的變換矩陣贷笛。通過下面兩個函數(shù)設置。

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

這兩個函數(shù)對應Layer中的setPosition和setMatrix函數(shù)宙项;這是上層WindowManager設置下來的乏苦。

  • 內(nèi)容屬性。定義顯示內(nèi)容如何呈現(xiàn),顯示的內(nèi)容也就是Buffer汇荐。
    Layer的顯示洞就,除了前面說道的幾個區(qū)域描述,很有很多結構進一步的描述才能最終顯示出來掀淘。包括諸如剪裁(用來擴展內(nèi)容的一部分以填充層的邊界)和轉換(用來顯示旋轉或翻轉的內(nèi)容)等信息旬蟋。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
如果再加上可見區(qū)域visibleRegion呢革娄?
再加上damageRegion呢倾贰?
還有Transform呢?

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


Android顯示區(qū)域間的關系

看圖說話:

  1. Layer區(qū)域和屏幕區(qū)域匆浙,就是Layer和屏幕本身的大小區(qū)域

  2. sourceCrop 剪切區(qū)域。
    sourceCrop是對Layer進行剪切的架忌,值截取部分Layer的內(nèi)容進行顯示吞彤;sourceCrop不超過Layer的大小我衬,超過沒有意義叹放。

  3. displayFrame 顯示區(qū)域。
    displayFrame表示Layer在屏幕上的顯示區(qū)域挠羔,具體說來井仰,是sourceCrop區(qū)域在顯示屏上的顯示區(qū)域。displayFrame一般來說破加,小于屏幕的區(qū)域俱恶。而displayFrame可能比sourceCrop大,可能小范舀,這都是正常的合是,只是需要做縮放,這就是合成時需要處理的锭环。

  4. visibleRegion 可見區(qū)域聪全。
    displayFrame 區(qū)域不一定都能看到的,如果存在上層Layer辅辩,那么displayFrame區(qū)域可能部分或全部被蓋住难礼,displayFrame沒有被蓋住的部分就是可見區(qū)域visibleRegion。

  5. damageRegion 受損區(qū)域玫锋,或者稱之為更新區(qū)域蛾茉。
    damageRegion表示Layer內(nèi)容被破壞的區(qū)域,也就是說這部分區(qū)域的內(nèi)容變了撩鹿,所以這個屬性一般是和上一幀相比時才有意義谦炬。這算是對合成的一種優(yōu)化,重新合成時节沦,我們只去合成damageRegion區(qū)域键思,其他的可見區(qū)域還是用的上一幀的數(shù)據(jù)窜管。

  6. visibleNonTransparentRegion 可見非透明區(qū)域。
    透明區(qū)域transparentRegion是可見區(qū)域visibleRegion的一部分稚机,只是這一部分透明的看到的是底層Layer的內(nèi)容幕帆。在SurfaceFlinger的Layer中定義visibleNonTransparentRegion,表示可見而又不透明的部分赖条。

  7. coveredRegion 被覆蓋的區(qū)域失乾。
    表示Layer被TopLayer覆蓋的區(qū)域,一看圖就很好理解纬乍。從圖中碱茁,你可以簡單的認為是displayFrame和TopLayer區(qū)域重合的部分。

注意, 這里之所以說簡單的認為仿贬,這是因為HWC空間的區(qū)域大小是SurfaceFlinger空間的區(qū)域經(jīng)過縮放纽竣,經(jīng)過Transform旋轉,移動等后才得出的茧泪,要是混淆了就理解不對了蜓氨。

  • 合成屬性。定義層應如何與其他層合成队伟。包括混合模式和用于 Alpha 合成的全層 Alpha 值等信息穴吹。
    總的說來,合成分為兩個大類:GPU合成嗜侮,HWC合成港令。根據(jù)具體的情況,分為下列幾類:
* 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數(shù)據(jù)顷霹,用GPU處理。
  2. Device HWC2硬件設備击吱,主要處理BufferLayer數(shù)據(jù)淋淀,用HWC處理
  3. SolidColor 固定顏色合成,主要處理ColorLayer數(shù)據(jù)姨拥,用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中,默認參數(shù)為true似芝。其實確定合成類型分3步那婉,第一步,SurfaceFlinger制定合成類型党瓮,callIntoHwc這個時候callIntoHwc為true详炬,將類型制定給HWC。第二步寞奸,HWC根據(jù)實際情況呛谜,看看SurfaceFlinger制定的合成類型能不能執(zhí)行,如果條件不滿足枪萄,做出修改隐岛;第三步,SurfaceFlinger根據(jù)HWC的修改情況瓷翻,再做調(diào)整聚凹,最終確認合成類型,這個時候callIntoHwc參數(shù)設置為false齐帚。

  • 優(yōu)化屬性妒牙。提供一些非必須的參數(shù),以供HWC進行合成的優(yōu)化童谒。包括層的可見區(qū)域以及層的哪個部分自上一幀以來已經(jīng)更新等信息单旁。也就是前面說到的visibleRegion,damageRegion等饥伊。

顯示屏Display

顯示屏Display是合成的另一個重要單元。系統(tǒng)可以具有多個顯示設備蔫饰,并且在正常系統(tǒng)操作期間可以添加或刪除顯示設備琅豆。該添加/刪除可以應 HWC 設備的熱插拔請求,或者應客戶端的請求進行篓吁,這允許創(chuàng)建虛擬顯示設備茫因,其內(nèi)容會渲染到離屏緩沖區(qū)(而不是物理顯示設備)。

HWC中杖剪,SurfaceFlinger中創(chuàng)建的Layer冻押,在合成開始時,將被指定到每個Display上盛嘿,此后合成過程中洛巢,每個Display合成指定給自己的Layer。

參考前面我們大的類圖:
SurfaceFlinger前端次兆,每個顯示屏稿茉,用DisplayDevice類描述,在后端顯示數(shù)據(jù)用DisplayData描述。而在HWC2的Client端漓库,定義了Display類進行描述恃慧。對于HWC2服務端則用hwc2_display_t描述,hwc2_display_t只是一個序號渺蒿,Vendor具體實現(xiàn)時痢士,才具體的去管理Display的信息。

HWC2 提供相應函數(shù)來確定給定顯示屏的屬性茂装,在不同配置(例如 4k 或 1080p 分辨率)和顏色模式(例如native顏色或真彩 sRGB)之間切換良瞧,以及打開、關閉顯示設備或?qū)⑵淝袚Q到低功率模式(如果支持)训唱。

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) 信號的控制为肮,以及對于客戶端的回調(diào)摊册,用于通知它何時發(fā)生 vsync 事件。

接口指針

HWC2 頭文件中颊艳,HWC 接口函數(shù)由 lowerCamelCase 命名慣例 定義茅特,但是這些函數(shù)名稱的字段并不實際存在于接口中。相反棋枕,幾乎每個函數(shù)都是通過使用 hwc2_device_t 提供的 getFunction 請求函數(shù)指針來進行加載白修。例如,函數(shù) createLayer 是一個 HWC2_PFN_CREATE_LAYER 類型的函數(shù)指針重斑,當枚舉值 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實現(xiàn)蠢琳,基本都是這樣,偽代碼:

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);
    
        // 具體的接口實現(xiàn)
    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 想要創(chuàng)建新層時泰讽,它會調(diào)用 createLayer 函數(shù),然后返回一個 hwc2_layer_t 類型的句柄,昔期。在此之后已卸,SurfaceFlinger 每次想要修改該層的屬性時,都會將該 hwc2_layer_t 值以及進行修改所需的任何其他信息傳遞給相應的修改函數(shù)硼一。hwc2_layer_t 類型句柄的大小足以容納一個指針或一個索引累澡,并且 SurfaceFlinger 會將其視為不透明,從而為 HWC 實現(xiàn)人員提供最大的靈活性般贼。

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服務分兩部分:

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/android.hardware.graphics.composer@2.1-service
    class hal animation
    user system
    group graphics drmrpc
    capabilities SYS_NICE
    onrestart restart surfaceflinge
  • 實現(xiàn)庫 android.hardware.graphics.composer@2.1-impl.so
    hwc的執(zhí)行程序中,注冊的IComposer叠洗,將調(diào)到對應的FETCH函數(shù)甘改,F(xiàn)ETCH函數(shù)實現(xiàn)及是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函數(shù)中惕味,才正在去加載Vendor的實現(xiàn)楼誓,通過統(tǒng)一的接口hw_get_module根據(jù)IDHWC_HARDWARE_MODULE_ID去加載。加載完成后名挥,創(chuàng)建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驅(qū)動主守,通過initWithFb初始化禀倔。如果是HWC驅(qū)動,通過initWithHwc初始化参淫。我們需要的是HWC2的接口救湖,如果不是HWC2的HAl實現(xiàn),那么需要做適配涎才。

FrameBuffer驅(qū)動鞋既,采用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;
}

這兩種適配的實現(xiàn)代碼如下:

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

分別打包成兩個動態(tài)so庫力九,Adapter中再調(diào)具體的Vendor實現(xiàn)。

初始化時邑闺,第一步先獲取Vendor實現(xiàn)的處理能力:

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的接口函數(shù):

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

根據(jù)ID陡舅,同過Vendor實現(xiàn)的getFunction函數(shù)抵乓,去獲取Vendor對應的函數(shù)地址。

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服務之間靶衍,很多函數(shù)灾炭,并沒有直接的調(diào)用,而是通過Buffer的讀寫來實現(xiàn)調(diào)用和參數(shù)的傳遞的颅眶。所以蜈出,Client端和Server端通信,基本通過以下相關的途徑:

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

Server端回調(diào)Client端涛酗,通過IComposerCallback.hal接口铡原。

hal接口的方式,比較好理解煤杀,其本質(zhì)就是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的接口函數(shù)比較多枯途,這里提供的接口忌怎,都是一些實時的接口,也就是Client需要Server立即響應的接口酪夷。

IComposer.hal接口

IComposer.hal就3個接口函數(shù)榴啸,createClient主要的是這里的createClient函數(shù),創(chuàng)建一個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回調(diào)給Client端的库说,主要是下面3個接口:

    onHotplug(Display display, Connection connected);

    oneway onRefresh(Display display);

    oneway onVsync(Display display, int64_t timestamp);

三個接口分別上報熱插拔,刷新請求片择,和Vsync事件潜的。另外,IComposerCallback接口需要通過IComposerClient.hal的registerCallback函數(shù)進行注冊字管。

command Buffer

相比前面的hal接口啰挪,這里的Buffer方式的調(diào)用都不是實時的信不,先將命令寫到Buffer中,最后再一次性的調(diào)用到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的數(shù)組來保存數(shù)據(jù)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函數(shù)為例卵牍,對應的command為SET_LAYER_Z_ORDER

SET_LAYER_Z_ORDER                  = 0x40a << OPCODE_SHIFT,

對應的函數(shù)為:

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類型的數(shù)組释牺。

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
    函數(shù)如下:
    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中主要是看我們寫的數(shù)據(jù)對不對,如果寫的太多虽风,超過mCommandEnd的截掉棒口。如果寫的太少,補0辜膝。

2.指定Layer
通過selectLayer函數(shù)指定layer陌凳;selectLayer函數(shù)和前面的selectDisplay類似:

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

3.將要設置的數(shù)據(jù)寫如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函數(shù)初橘。

到此验游,z-order寫到Buffer mData中充岛。注意,只是寫到了mData中耕蝉,還沒有傳到HWC的服務端呢崔梗。

4.執(zhí)行命令
Buffer中的數(shù)據(jù)什么時候才傳到HWC的服務端呢? 答案是 executeCommands的時候垒在。execute 基本就是validate和present處理的時候會先調(diào)蒜魄,execute將Buffer命令真正是傳到Server進行解析,調(diào)相應的接口场躯。execute主要是通過IComposerClient.hal的實時接口來完成谈为。

execute函數(shù)如下:

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來實現(xiàn),其作用就是將前面我們已經(jīng)寫到Buffer中的command及數(shù)據(jù)踢关,寫到命令隊列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根據(jù)隊列的文件描述符去找對應的隊列签舞。
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會根據(jù)描述符創(chuàng)建對應的命令隊形秕脓。

  • 執(zhí)行命令隊列
    executeCommands在server端的實現(xiàn)為:
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讀取命令隊列,將命令儒搭,數(shù)據(jù)等從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 解析命令怔檩,根據(jù)命令,解析具體的數(shù)據(jù)蓄诽。比如我們設置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時數(shù)據(jù)才真正傳遞到Server中薛训,生效。z-order通過mHal的setLayerZOrder設置到Vendor的HAL實現(xiàn)中仑氛。

endCommand 表示數(shù)據(jù)讀取完乙埃,記錄讀取的位置。

回到啊Client端execute函數(shù)锯岖。Client端的Reader也會讀取返回值介袜,

HWC2 中Fence的更改

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

在 HWC v1.x 中出吹,釋放Fence和退出Fence是推測性的遇伞。在幀 N 中檢索到的Buffer的釋放Fence或顯示設備的退出Fence不會先于在幀 N + 1 中檢索到的Fence變?yōu)橛|發(fā)狀態(tài)。換句話說捶牢,該Fence的含義是“不再需要您為幀 N 提供的Buffer內(nèi)容”鸠珠。這是推測性的巍耗,因為在理論上,SurfaceFlinger 在幀 N 之后的一段不確定的時間內(nèi)可能無法再次運行渐排,這將使得這些柵欄在該時間段內(nèi)不會變?yōu)橛|發(fā)狀態(tài)炬太。

在 HWC 2.0 中,釋放Fence和退出Fence是非推測性的驯耻。在幀 N 中檢索到的釋放Fence或退出Fence亲族,將在相關Buffer的內(nèi)容替換幀 N - 1 中緩沖區(qū)的內(nèi)容后立即變?yōu)橛|發(fā)狀態(tài),或者換句話說可缚,該Fence的含義是“您為幀 N 提供的緩沖區(qū)內(nèi)容現(xiàn)在已經(jīng)替代以前的內(nèi)容”霎迫。這是非推測性的,因為在硬件呈現(xiàn)此幀的內(nèi)容之后城看,該柵欄應該在 presentDisplay 被調(diào)用后立即變?yōu)橛|發(fā)狀態(tài)女气。

小結

這里主要是總結性的介紹一下HWC2,很多流程测柠,稍后我們在代碼中具體來分析炼鞠。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市轰胁,隨后出現(xiàn)的幾起案子谒主,更是在濱河造成了極大的恐慌,老刑警劉巖赃阀,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霎肯,死亡現(xiàn)場離奇詭異,居然都是意外死亡榛斯,警方通過查閱死者的電腦和手機观游,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驮俗,“玉大人懂缕,你說我怎么就攤上這事⊥醮眨” “怎么了搪柑?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長索烹。 經(jīng)常有香客問我工碾,道長,這世上最難降的妖魔是什么百姓? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任渊额,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘端圈。我一直安慰自己焦读,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布舱权。 她就那樣靜靜地躺著,像睡著了一般仑嗅。 火紅的嫁衣襯著肌膚如雪宴倍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天仓技,我揣著相機與錄音鸵贬,去河邊找鬼。 笑死脖捻,一個胖子當著我的面吹牛阔逼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播地沮,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼嗜浮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了摩疑?” 一聲冷哼從身側響起危融,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雷袋,沒想到半個月后吉殃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡楷怒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年蛋勺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸠删。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡抱完,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冶共,到底是詐尸還是另有隱情乾蛤,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布捅僵,位于F島的核電站家卖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏庙楚。R本人自食惡果不足惜上荡,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酪捡,春花似錦叁征、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至永罚,卻和暖如春啤呼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呢袱。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工官扣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人羞福。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓两芳,卻偏偏與公主長得像挪蹭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容