神刀安全網

Android P 圖形顯示系統(一)硬件合成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 P 圖形顯示系統(一)硬件合成HWC2

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 P 圖形顯示系統(一)硬件合成HWC2

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相關的類,接下再來我們一下進行介紹。

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

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),如下圖:

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

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相關的類圖:

Android P 圖形顯示系統(一)硬件合成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實現中時,代碼實現時,采用的方式。

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

圖層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的集合,其間的關系如下圖:

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

圖層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就是變換矩陣,它是一個3×3的矩陣。相關類的關系如下:

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

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 P 圖形顯示系統(一)硬件合成HWC2

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。

相關的類圖如下:

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

命令讀寫器

相關的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,很多流程,稍后我們在代碼中具體來分析。

轉載本站任何文章請注明:轉載至神刀安全網,謝謝神刀安全網 » Android P 圖形顯示系統(一)硬件合成HWC2

分享到:更多 ()
河北11选5开奖查询