HWC概述
HWC
(hwcomposer)是Android中進行窗口(Layer
)合成和顯示的HAL層模塊欣舵,其實現(xiàn)是特定于設(shè)備的视粮,而且通常由顯示設(shè)備制造商 (OEM)完成讨永,為SurfaceFlinger
服務(wù)提供硬件支持。
SurfaceFlinger
可以使用OpenGL ES
合成Layer
,這需要占用并消耗GPU資源铣除。大多數(shù)GPU都沒有針對圖層合成進行優(yōu)化,當(dāng)SurfaceFlinger
通過GPU合成圖層時鹦付,應(yīng)用程序無法使用GPU進行自己的渲染尚粘。而HWC
通過硬件設(shè)備進行圖層合成,可以減輕GPU的合成壓力敲长。
顯示設(shè)備的能力千差萬別背苦,很難直接用API表示硬件設(shè)備支持合成的Layer
數(shù)量互捌,Layer
是否可以進行旋轉(zhuǎn)和混合模式操作,以及對圖層定位和硬件合成的限制等行剂。因此HWC描述上述信息的流程是這樣的:
-
SurfaceFlinger
向HWC
提供所有Layer
的完整列表秕噪,讓HWC
根據(jù)其硬件能力,決定如何處理這些Layer
厚宰。 -
HWC
會為每個Layer
標注合成方式江耀,是通過GPU還是通過HWC
合成撬讽。 -
SurfaceFlinger
負責(zé)先把所有注明GPU合成的Layer
合成到一個輸出Buffer智绸,然后把這個輸出Buffer和其他Layer
(注明HWC合成的Layer)一起交給HWC
从绘,讓HWC
完成剩余Layer
的合成和顯示。
雖然每個顯示設(shè)備的能力不同撵幽,但是官方要求每個HWC
硬件模塊都應(yīng)該支持以下能力:
- 至少支持4個疊加層:狀態(tài)欄灯荧、系統(tǒng)欄、應(yīng)用本身和壁紙或者背景盐杂。
- 疊加層可以大于顯示屏逗载,例如:壁紙
- 同時支持預(yù)乘每像素(per-pixel)Alpha混合和每平面(per-plane)Alpha混合。
- 為了支持受保護的內(nèi)容链烈,必須提供受保護視頻播放的硬件路徑厉斟。
- RGBA packing order, YUV formats, and tiling, swizzling, and stride properties
Tiling:可以把Image切割成MxN個小塊,最后渲染時强衡,再將這些小塊拼接起來擦秽,就像鋪瓷磚一樣。
Swizzling:一種拌和技術(shù)漩勤,表示向量單元可以被任意地重排或重復(fù)感挥。
但是并非所有情況下HWC
都比GPU更高效,例如:當(dāng)屏幕上沒有任何變化時越败,尤其是疊加層有透明像素并且需要進行圖層透明像素混合時触幼。在這種情況下,HWC
可以要求部分或者全部疊加層都進行GPU合成眉尸,然后HWC
持有合成的結(jié)果Buffer域蜗,如果SurfaceFlinger
要求合成相同的疊加圖層列表巨双,HWC
可以直接顯示之前合成的結(jié)果Buffer噪猾,這有助于提高待機設(shè)備的電池壽命。
HWC
也提供了VSync
事件筑累,用于管理渲染和圖層合成時機袱蜡,后續(xù)文章會進行介紹。
HWC2實現(xiàn)
Android7.0提供了HWC和HWC2兩個版本慢宗,默認使用HWC坪蚁,但是手機廠商也可以選擇HWC2奔穿,如下所示:
// frameworks\native\services\surfaceflinger\Android.mk
USE_HWC2 := false
ifeq ($(USE_HWC2),true)
LOCAL_CFLAGS += -DUSE_HWC2
LOCAL_SRC_FILES += \
SurfaceFlinger.cpp \
DisplayHardware/HWComposer.cpp
else
LOCAL_SRC_FILES += \
SurfaceFlinger_hwc1.cpp \
DisplayHardware/HWComposer_hwc1.cpp
endif
SurfaceFlinger
通過HWComposer
使用HWC
硬件能力,HWComposer
構(gòu)造函數(shù)通過loadHwcModule
方法加載HWC模塊敏晤,并封裝成HWC2::Device
結(jié)構(gòu)贱田,如下所示:
// Load and prepare the hardware composer module,HWComposer構(gòu)造函數(shù)中通過此方法加載HWC模塊
void HWComposer::loadHwcModule()
{
// 定義在hardware.h中,表示一個硬件模塊嘴脾,是HAL層的靈魂
hw_module_t const* module;
// 加載硬件廠商提供的hwcomposer模塊男摧,HWC_HARDWARE_MODULE_ID定義在hwcomposer_defs.h中,表示"hwcomposer"
if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
ALOGE("%s module not found, aborting", HWC_HARDWARE_MODULE_ID);
abort();
}
hw_device_t* device = nullptr;
// 通過硬件廠商提供的open函數(shù)打開一個"composer"硬件設(shè)備译打,HWC_HARDWARE_COMPOSER也定義在hwcomposer_defs.h中耗拓,表示"composer"
int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);
if (error != 0) {
ALOGE("Failed to open HWC device (%s), aborting", strerror(-error));
abort();
}
uint32_t majorVersion = (device->version >> 24) & 0xF;
// mHwcDevice是HWC2.h中定義的HWC2::Device,所有與HWC的交互都通過mHwcDevice
if (majorVersion == 2) { // HWC2奏司,hwc2_device_t是hwcomposer2.h中的結(jié)構(gòu)體
mHwcDevice = std::make_unique<HWC2::Device>(
reinterpret_cast<hwc2_device_t*>(device));
} else { // 設(shè)備是基于HWC1乔询,這里用HWC2去適配,Android7.0及以前默認都是HWC1韵洋,hwc_composer_device_1_t是hwcomposer.h中的結(jié)構(gòu)體
mAdapter = std::make_unique<HWC2On1Adapter>(
reinterpret_cast<hwc_composer_device_1_t*>(device));
mHwcDevice = std::make_unique<HWC2::Device>(
static_cast<hwc2_device_t*>(mAdapter.get()));
}
// 獲取硬件支持的最大虛擬屏幕數(shù)量竿刁,VirtualDisplay可用于錄屏
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}
先加載
hwcomposer
模塊得到hw_module_t
,再打開composer
設(shè)備得到hw_device_t
麻献。hw_module_t
和hw_device_t
定義在hardware\libhardware\include\hardware\hardware.h们妥,表示一個HAL層模塊和屬于該模塊的一個實現(xiàn)設(shè)備。注意這里是先有HAL模塊勉吻,再有實現(xiàn)此模塊的硬件設(shè)備监婶。
上述通過hw_get_module
方法(hardware\libhardware\hardware.c)加載hwcomposer
模塊,此模塊由硬件廠商提供實現(xiàn)齿桃,例如:hardware\libhardware\modules\hwcomposer\hwcomposer.cpp是hwcomposer
模塊基于HWC1的default實現(xiàn)惑惶,對應(yīng)的共享庫是hwcomposer.default.so
;hardware\qcom\display\msm8994\libhwcomposer\hwc.cpp是高通MSM8994基于HWC1的實現(xiàn)短纵,對應(yīng)的共享庫是hwcomposer.msm8994.so
带污。
如果是基于HWC2協(xié)議實現(xiàn),則需要實現(xiàn)hwcomposer2.h中定義的hwc2_device_t
接口香到,例如:class VendorComposer : public hwc2_device_t
鱼冀。Android7.0的hwcomposer
模塊默認都是基于HWC1協(xié)議實現(xiàn)的。
每個HAL層模塊實現(xiàn)都要定義一個HAL_MODULE_INFO_SYM
數(shù)據(jù)結(jié)構(gòu)悠就,并且該結(jié)構(gòu)的第一個字段必須是hw_module_t
千绪,下面是高通MSM8994hwcomposer
模塊的定義:
// 高通MSM8994
hwc_module_t HAL_MODULE_INFO_SYM = {
// common表示hw_module_t模塊
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 2,
version_minor: 0,
id: HWC_HARDWARE_MODULE_ID, // hwcomposer
name: "Qualcomm Hardware Composer Module",
author: "CodeAurora Forum",
methods: &hwc_module_methods,
dso: 0,
reserved: {0},
}
};
最重要的一點:
HWComposer::loadHwcModule
方法最終把HWC模塊封裝成了HWC2::Device
。
frameworks\native\services\surfaceflinger\DisplayHardware\HWC2.h主要定義了以下三個結(jié)構(gòu)體:
-
HWC2::Device
:表示硬件合成顯示設(shè)備 -
HWC2::Display
:表示一個顯示屏幕梗脾,可以是物理顯示屏(可以熱插拔接入或者移除)荸型,也可以是虛擬顯示屏,現(xiàn)在的游戲錄屏一般都是基于虛擬屏幕實現(xiàn)的炸茧。 -
HWC2::Layer
:表示一個疊加圖層瑞妇,對應(yīng)與應(yīng)用側(cè)的Surface稿静。
它們是對HWC
硬件模塊的進一步封裝,方便進行調(diào)用辕狰。HWC2::Device
持有一個hwc2_device_t
改备,用于連接硬件設(shè)備,它包含了很多HWC2_PFN開頭的函數(shù)指針變量蔓倍,這些函數(shù)指針定義在hwcomposer2.h
绍妨。
在HWC2::Device
的構(gòu)造函數(shù)中,會通過Device::loadFunctionPointers
-> loadFunctionPointer(FunctionDescriptor desc, PFN& outPFN)
-> hwc2_device_t::getFunction
的調(diào)用鏈從硬件設(shè)備中獲取具體的函數(shù)指針實現(xiàn)柬脸。關(guān)鍵模板函數(shù)如下所示:
// 模板函數(shù)他去,用于向硬件設(shè)備查詢具體的函數(shù)指針實現(xiàn)
template <typename PFN>
[[clang::warn_unused_result]] bool loadFunctionPointer(
FunctionDescriptor desc, PFN& outPFN) {
// desc表示一個枚舉類型值
auto intDesc = static_cast<int32_t>(desc);
// mHwcDevice表示hwc2_device_t,是硬件驅(qū)動提供的實現(xiàn)
auto pfn = mHwcDevice->getFunction(mHwcDevice, intDesc);
if (pfn != nullptr) {
// 強轉(zhuǎn)函數(shù)指針
outPFN = reinterpret_cast<PFN>(pfn);
return true;
} else {
ALOGE("Failed to load function %s", to_string(desc).c_str());
return false;
}
}
這些函數(shù)指針主要分為三類:
- 硬件設(shè)備(Device)相關(guān)的函數(shù)指針
- 顯示屏幕(Display)相關(guān)的函數(shù)指針
- 疊加圖層(Layer)相關(guān)的函數(shù)指針
通過上述函數(shù)指針可以與hwc2_device_t
表示的硬件合成模塊進行交互倒堕。三類指針分別選取了一個示例:
// Device方法:獲得設(shè)備支持的最大虛擬屏幕個數(shù)
uint32_t Device::getMaxVirtualDisplayCount() const
{
return mGetMaxVirtualDisplayCount(mHwcDevice);
}
// Display方法:為指定Device的指定Display創(chuàng)建一個Layer
Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
{
// 表示創(chuàng)建的layer的唯一標識符
hwc2_layer_t layerId = 0;
// mDevice.mHwcDevice表示hwc2_device_t灾测,即真正的硬件設(shè)備,mId表示當(dāng)前Display的唯一標識符垦巴,即為指定Device的指定Display創(chuàng)建一個Layer
int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
}
// 基于layerId創(chuàng)建HWC2::Layer
auto layer = std::make_shared<Layer>(shared_from_this(), layerId);
// 保存當(dāng)前Display所有的Layer
mLayers.emplace(layerId, layer);
// 返回創(chuàng)建的HWC2::Layer
*outLayer = std::move(layer);
return Error::None;
}
// Layer方法:為指定Device的指定Display的指定Layer指定合成方式
Error Layer::setCompositionType(Composition type)
{
auto intType = static_cast<int32_t>(type);
// 為指定Device的指定Display的指定Layer指定合成方式
int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,
mDisplayId, mId, intType);
return static_cast<Error>(intError);
}
可以通過類圖媳搪,直觀感受下引用關(guān)系。
HWC2::Device
構(gòu)造函數(shù)除了完成獲取函數(shù)指針實現(xiàn)以外骤宣,還會通過Device::registerCallbacks
向硬件設(shè)備注冊三個Display
的回調(diào):熱插拔秦爆,刷新和VSync信號,如下所示:
// HWC硬件的幾種回調(diào)描述符
// hardware\libhardware\include\hardware\hwcomposer2.h
enum class Callback : int32_t {
Invalid = HWC2_CALLBACK_INVALID,
// 顯示屏幕(Display)的熱插拔
Hotplug = HWC2_CALLBACK_HOTPLUG,
// 顯示屏幕(Display)的刷新
Refresh = HWC2_CALLBACK_REFRESH,
// 顯示屏幕(Display)的VSync信號
Vsync = HWC2_CALLBACK_VSYNC,
};
// 注冊熱插拔/刷新/VSync回調(diào)
void Device::registerCallbacks()
{ // Callback枚舉類型如上所示
registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
}
// 模板函數(shù)憔披,用于向硬件設(shè)備(hwc2_device_t)注冊函數(shù)回調(diào)
template <typename PFN, typename HOOK>
void registerCallback(Callback callback, HOOK hook) {
// Callback枚舉類型如上所示
auto intCallback = static_cast<int32_t>(callback);
// this表示HWC2::Device, hwc2_callback_data_t表示void指針類型
auto callbackData = static_cast<hwc2_callback_data_t>(this);
// 把函數(shù)指針強轉(zhuǎn)成統(tǒng)一的void (*)() 函數(shù)指針類型
auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
// 向hwc2_device_t注冊函數(shù)回調(diào)等限,callbackData表示透傳的資源
mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
}
// 以VSync的靜態(tài)函數(shù)為例
static void vsync_hook(hwc2_callback_data_t callbackData,
hwc2_display_t displayId, int64_t timestamp) {
// callbackData表示透傳的void指針,實際指HWC2::Device
auto device = static_cast<HWC2::Device*>(callbackData);
// 通過displayId獲取對應(yīng)的HWC2::Display
auto display = device->getDisplayById(displayId);
if (display) {
// 向外回調(diào)
device->callVsync(std::move(display), timestamp);
} else {
ALOGE("Vsync callback called with unknown display %" PRIu64, displayId);
}
}
void Device::callVsync(std::shared_ptr<Display> display, nsecs_t timestamp)
{
// 通過std::function可調(diào)用對象mVsync向外回調(diào)芬膝,該可調(diào)用對象實際是HWComposer通過Device::registerVsyncCallback方法注冊的
if (mVsync) {
mVsync(std::move(display), timestamp);
} else {
mPendingVsyncs.emplace_back(std::move(display), timestamp);
}
}
總結(jié)一下望门,HWC2::Device
構(gòu)造函數(shù)向硬件設(shè)備注冊三個Display
回調(diào):熱插拔,刷新和VSync信號锰霜。當(dāng)HWC2::Device
收到這些回調(diào)時筹误,會通過監(jiān)聽器向外回調(diào)到對應(yīng)的HWComposer函數(shù):HWComposer::hotplug
/HWComposer::invalidate
/HWComposer::vsync
。HWComposer再通過這些信息驅(qū)動對應(yīng)工作癣缅,后續(xù)文章進行介紹厨剪。
HWC Layer合成方式
上文提到HWC2::Device
中的函數(shù)指針是hardware\libhardware\include\hardware\hwcomposer2.h中定義的,除此之外友存,該頭文件還定義了一些重要的結(jié)構(gòu)體祷膳,這里介紹兩個比較重要的:
// 顯示屏類型
enum class DisplayType : int32_t {
Invalid = HWC2_DISPLAY_TYPE_INVALID,
// 物理顯示屏,顯示設(shè)備有一個主屏幕爬立,然后可以通過熱插拔添加或者刪除外接顯示屏
Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
// 虛擬顯示屏钾唬,內(nèi)容會渲染到離屏緩沖區(qū)万哪,Android錄屏功能就是基于虛擬屏實現(xiàn)的
Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
};
// Layer合成類型侠驯,HWC2_COMPOSITION_XX取自hwc2_composition_t枚舉
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,
};
DisplayType
表示顯示屏類型抡秆,上面注釋已經(jīng)介紹,重點看下Layer合成類型:
- Client:這里的Client是相對于HWC硬件設(shè)備來說的吟策,即不通過HWC來合成圖層儒士,而是通過GPU先把所有的這類圖層合成到client target buffer(一個離屏的圖形緩沖區(qū),buffer_handle_t表示指向這塊顯存的指針檩坚,顯存由
Gralloc
模塊分配)着撩,然后再通過Display::setClientTarget
把這塊圖形Buffer的地址傳遞給HWC設(shè)備,最后由HWC設(shè)備把其他Layer和這個圖形Buffer進一步合成匾委,并最終展示在Display上拖叙。 - Device:通過HWC硬件來合成圖層,默認情況下赂乐,
SurfaceFlinger
會配置每個Layer都通過Device
方式合成薯鳍,但是HWC設(shè)備會根據(jù)硬件設(shè)備的性能改變某些圖層的合成方式。 - SolidColor:HWC設(shè)備將通過
Layer::setColor
設(shè)置的顏色渲染這個圖層挨措,如果HWC設(shè)備不支持這種合成方式挖滤,那么將會請求SurfaceFlinger
改變合成方式為Client。 - Cursor:與Device類似浅役,但是這個圖層的位置可以通過setCursorPosition異步設(shè)置斩松。如果HWC設(shè)備不支持這種合成方式,那么將會請求
SurfaceFlinger
改變合成方式為Client或者Device觉既。 - Sideband:HWC硬件會處理該類圖層的合成惧盹,以及它的緩沖區(qū)更新和內(nèi)容同步,但是只有擁有
HWC2_CAPABILITY_SIDEBAND_STREAM
能力的設(shè)備才支持這種圖層瞪讼,若設(shè)備不支持岭参,那么將會請求SurfaceFlinger
改變合成方式為Client或者Device。
那么一個Layer
的合成方式是怎么確定的那尝艘?大致流程如下所示:
- 當(dāng)VSync信號到來時演侯,SurfaceFlinger被喚醒,處理Layer的新建背亥,銷毀和更新秒际,并且為相應(yīng)Layer設(shè)置期望的合成方式。
- 所有Layer更新后狡汉,SurfaceFlinger調(diào)用
validateDisplay
娄徊,讓HWC決定每個Layer的合成方式。 - SurfaceFlinger調(diào)用
getChangedCompositionTypes
檢查HWC是否對任何Layer的合成方式做出了改變盾戴,若是寄锐,那么SurfaceFlinger則調(diào)整對應(yīng)Layer的合成方式,并且調(diào)用acceptDisplayChanges
通知HWC。 - SurfaceFlinger把所有
Client
類型的Layer合成到Target圖形緩沖區(qū)橄仆,然后調(diào)用setClientTarget
把Target Buffer設(shè)置給HWC剩膘。(如果沒有Client類型的Layer,則可以跳過該方法) - 最后盆顾,SurfaceFlinger調(diào)用
presentDisplay
怠褐,讓HWC完成剩余Layer的合成,并且在顯示屏上展示出最終的合成結(jié)果您宪。
總結(jié)
本篇文章只是簡單介紹了HWC模塊的相關(guān)類:HWComposer
奈懒、HWC2::Device
、HWC2::Display
和HWC2::Layer
宪巨,以及它們的關(guān)系磷杏。此外,還著重介紹了Layer的合成方式和合成流程捏卓。后續(xù)文章會更加全面的介紹SurfaceFlinger
是如何通過HWC模塊完成Layer合成和上屏的(虛擬屏幕是到離屏緩沖區(qū))茴丰。
相關(guān)源碼
- hardware\libhardware\include\hardware\hardware.h
- hardware\libhardware\hardware.c
- hardware\libhardware\include\hardware\hwcomposer_defs.h
- hardware\libhardware\include\hardware\hwcomposer.h
- hardware\libhardware\include\hardware\hwcomposer2.h
- frameworks\native\services\surfaceflinger\DisplayHardware\HWC2.h
- frameworks\native\services\surfaceflinger\DisplayHardware\HWC2.cpp
- frameworks\native\services\surfaceflinger\DisplayHardware\HWComposer.h
- frameworks\native\services\surfaceflinger\DisplayHardware\HWComposer.cpp
- frameworks\native\services\surfaceflinger\SurfaceFlinger.h
- frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp