[TOC]
硬件合成HWC2
Hardware Composer HAL (HWC) 是 SurfaceFlinger 用來將 Surface 合成到屏幕。HWC 可以抽象出疊加層和 2D 位塊傳送器等,其主要是幫忙GPU完成一些工作。
SurfaceFlinger是一個系統服務,其作用是接受來自多個源的Buffer數據,對它們進行合成,然后發送到顯示設備進行顯示。在之前的Android版本中,顯示基本都是基于硬件的FrameBuffer來實現的,例如/dev/graphics/fb0
,但是在后來的版本中,實現可以多樣化了,比如高通采用SDM,其他有些平臺采用ADF,DRM等。
SurfaceFlinger和HWC的相關配合,實現Android系統的合成與顯示~
SurfaceFlinger概述
大多數應用通常在屏幕上有三個層:屏幕頂部的狀態欄、底部或側面的導航欄以及應用的界面。有些應用會擁有更多或更少的層(例如,默認主屏幕應用有一個單獨的壁紙層,而全屏游戲可能會隱藏狀態欄)。每個層都可以單獨更新。狀態欄和導航欄由系統進程渲染,而應用層由應用渲染,兩者之間不進行協調。
下面我們通過一張圖,來進行說明:
從上圖,我們可以看出,在長按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 官網:
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相關的類,接下再來我們一下進行介紹。
其他的都好理解,有兩個地方需要注意:
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),如下圖:
- 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的實現。這部分包含三部分:接口,實現和服務,以動態庫的形式存在:
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的實現
這部分是HWC的具體實現,這部分的實現由硬件廠商完成。比如高通平臺,代碼位置一般為:
hardware/qcom/display
需要注意的是,HWC必須采用Binderized HAL模式,但是,并沒有要求一定要實現HWC2的HAL版本。HWC2的實現需要配置,以Android 8.0為例:
- 添加宏定義
TARGET_USES_HWC2
- 編譯打包HWC2相關的so庫
- SeLinux相關的權限添加
- 配置
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 中提供了幾個數據結構來描述合成以及可以顯示設備的就交互,比如圖層(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實現中時,代碼實現時,采用的方式。
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的集合,其間的關系如下圖:
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的矩陣。相關類的關系如下:
每一個Layer的都有兩個狀態:mCurrentState和mDrawingState,mCurrentState是給SurfaceFlinger的前端準備數據,mDrawingState是給將數據給到合成;每個狀態有兩個Geometry的描述requested和active,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;
};
怎么理解這里的sourceCrop和 displayFrame?
如果再加上可見區域visibleRegion呢?
再加上damageRegion呢?
還有Transform呢?
我們來個神圖,讓你一下子就能明白:
看圖說話:
Layer區域和屏幕區域,就是Layer和屏幕本身的大小區域
sourceCrop 剪切區域。
sourceCrop是對Layer進行剪切的,值截取部分Layer的內容進行顯示;sourceCrop不超過Layer的大小,超過沒有意義。displayFrame 顯示區域。
displayFrame表示Layer在屏幕上的顯示區域,具體說來,是sourceCrop區域在顯示屏上的顯示區域。displayFrame一般來說,小于屏幕的區域。而displayFrame可能比sourceCrop大,可能小,這都是正常的,只是需要做縮放,這就是合成時需要處理的。visibleRegion 可見區域。
displayFrame 區域不一定都能看到的,如果存在上層Layer,那么displayFrame區域可能部分或全部被蓋住,displayFrame沒有被蓋住的部分就是可見區域visibleRegion。damageRegion 受損區域,或者稱之為更新區域。
damageRegion表示Layer內容被破壞的區域,也就是說這部分區域的內容變了,所以這個屬性一般是和上一幀相比時才有意義。這算是對合成的一種優化,重新合成時,我們只去合成damageRegion區域,其他的可見區域還是用的上一幀的數據。visibleNonTransparentRegion 可見非透明區域。
透明區域transparentRegion是可見區域visibleRegion的一部分,只是這一部分透明的看到的是底層Layer的內容。在SurfaceFlinger的Layer中定義visibleNonTransparentRegion,表示可見而又不透明的部分。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,
};
- Client 相對HWC2硬件合成的概念,主要是處理BufferLayer數據,用GPU處理。
- Device HWC2硬件設備,主要處理BufferLayer數據,用HWC處理
- SolidColor 固定顏色合成,主要處理ColorLayer數據,用HWC處理或GPU處理。
- Cursor 鼠標標識合成,主要處理鼠標等圖標,用HWC處理或GPU處理
- 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服務分兩部分:
-
可以執行程序 android.hardware.graphics.composer@2.1-service
其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,
¶m) != 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
-
實現庫 android.hardware.graphics.composer@2.1-impl.so
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,很多流程,稍后我們在代碼中具體來分析。