HWC
(hwcomposer)是Android中進行窗口(Layer
)合成和顯示的HAL層模塊,其實現是特定於設備的,並且一般由顯示設備製造商 (OEM)完成,爲SurfaceFlinger
服務提供硬件支持。android
SurfaceFlinger
可使用OpenGL ES
合成Layer
,這須要佔用並消耗GPU資源。大多數GPU都沒有針對圖層合成進行優化,當SurfaceFlinger
經過GPU合成圖層時,應用程序沒法使用GPU進行本身的渲染。而HWC
經過硬件設備進行圖層合成,能夠減輕GPU的合成壓力。數據結構
顯示設備的能力千差萬別,很難直接用API表示硬件設備支持合成的Layer
數量,Layer
是否能夠進行旋轉和混合模式操做,以及對圖層定位和硬件合成的限制等。所以HWC描述上述信息的流程是這樣的:composer
SurfaceFlinger
向HWC
提供全部Layer
的完整列表,讓HWC
根據其硬件能力,決定如何處理這些Layer
。 HWC
會爲每一個Layer
標註合成方式,是經過GPU仍是經過HWC
合成。 SurfaceFlinger
負責先把全部註明GPU合成的Layer
合成到一個輸出Buffer,而後把這個輸出Buffer和其餘Layer
(註明HWC合成的Layer)一塊兒交給HWC
,讓HWC
完成剩餘Layer
的合成和顯示。 雖然每一個顯示設備的能力不一樣,可是官方要求每一個HWC
硬件模塊都應該支持如下能力:異步
Tiling:能夠把Image切割成MxN個小塊,最後渲染時,再將這些小塊拼接起來,就像鋪瓷磚同樣。ide
Swizzling:一種拌和技術,表示向量單元能夠被任意地重排或重複。函數
可是並不是全部狀況下HWC
都比GPU更高效,例如:當屏幕上沒有任何變化時,尤爲是疊加層有透明像素而且須要進行圖層透明像素混合時。在這種狀況下,HWC
能夠要求部分或者所有疊加層都進行GPU合成,而後HWC
持有合成的結果Buffer,若是SurfaceFlinger
要求合成相同的疊加圖層列表,HWC
能夠直接顯示以前合成的結果Buffer,這有助於提升待機設備的電池壽命。性能
HWC
也提供了VSync
事件,用於管理渲染和圖層合成時機,後續文章會進行介紹。優化
Android7.0提供了HWC和HWC2兩個版本,默認使用HWC,可是手機廠商也能夠選擇HWC2,以下所示:ui
// 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
構造函數經過loadHwcModule
方法加載HWC模塊,並封裝成HWC2::Device
結構,以下所示:this
// Load and prepare the hardware composer module,HWComposer構造函數中經過此方法加載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函數打開一個"composer"硬件設備,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中的結構體
mHwcDevice = std::make_unique<HWC2::Device>(
reinterpret_cast<hwc2_device_t*>(device));
} else { // 設備是基於HWC1,這裏用HWC2去適配,Android7.0及之前默認都是HWC1,hwc_composer_device_1_t是hwcomposer.h中的結構體
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()));
}
// 獲取硬件支持的最大虛擬屏幕數量,VirtualDisplay可用於錄屏
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}複製代碼
先加載
hwcomposer
模塊獲得hw_module_t
,再打開composer
設備獲得hw_device_t
。hw_module_t
和hw_device_t
定義在hardwarelibhardwareincludehardwarehardware.h,表示一個HAL層模塊和屬於該模塊的一個實現設備。注意這裏是先有HAL模塊,再有實現此模塊的硬件設備。
上述經過hw_get_module
方法(hardwarelibhardwarehardware.c)加載hwcomposer
模塊,此模塊由硬件廠商提供實現,例如:hardwarelibhardwaremoduleshwcomposerhwcomposer.cpp是hwcomposer
模塊基於HWC1的default實現,對應的共享庫是hwcomposer.default.so
;hardwareqcomdisplaymsm8994libhwcomposerhwc.cpp是高通MSM8994基於HWC1的實現,對應的共享庫是hwcomposer.msm8994.so
。若是是基於HWC2協議實現,則須要實現hwcomposer2.h中定義的hwc2_device_t
接口,例如:class VendorComposer : public hwc2_device_t
。Android7.0的hwcomposer
模塊默認都是基於HWC1協議實現的。每一個HAL層模塊實現都要定義一個HAL_MODULE_INFO_SYM
數據結構,而且該結構的第一個字段必須是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
。
frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.h主要定義瞭如下三個結構體:
HWC2::Device
:表示硬件合成顯示設備 HWC2::Display
:表示一個顯示屏幕,能夠是物理顯示屏(能夠熱插拔接入或者移除),也能夠是虛擬顯示屏,如今的遊戲錄屏通常都是基於虛擬屏幕實現的。 HWC2::Layer
:表示一個疊加圖層,對應與應用側的Surface。 它們是對HWC
硬件模塊的進一步封裝,方便進行調用。HWC2::Device
持有一個hwc2_device_t
,用於鏈接硬件設備,它包含了不少HWC2_PFN開頭的函數指針變量,這些函數指針定義在`hwcomposer2.h`。在HWC2::Device
的構造函數中,會經過Device::loadFunctionPointers
-> loadFunctionPointer(FunctionDescriptor desc, PFN& outPFN)
-> hwc2_device_t::getFunction
的調用鏈從硬件設備中獲取具體的函數指針實現。關鍵模板函數以下所示:
// 模板函數,用於向硬件設備查詢具體的函數指針實現
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,是硬件驅動提供的實現
auto pfn = mHwcDevice->getFunction(mHwcDevice, intDesc);
if (pfn != nullptr) {
// 強轉函數指針
outPFN = reinterpret_cast<PFN>(pfn);
return true;
} else {
ALOGE("Failed to load function %s", to_string(desc).c_str());
return false;
}
}複製代碼
這些函數指針主要分爲三類:
經過上述函數指針能夠與hwc2_device_t
表示的硬件合成模塊進行交互。三類指針分別選取了一個示例:
// Device方法:得到設備支持的最大虛擬屏幕個數
uint32_t Device::getMaxVirtualDisplayCount() const
{
return mGetMaxVirtualDisplayCount(mHwcDevice);
}
// Display方法:爲指定Device的指定Display建立一個Layer
Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
{
// 表示建立的layer的惟一標識符
hwc2_layer_t layerId = 0;
// mDevice.mHwcDevice表示hwc2_device_t,即真正的硬件設備,mId表示當前Display的惟一標識符,即爲指定Device的指定Display建立一個Layer
int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
}
// 基於layerId建立HWC2::Layer
auto layer = std::make_shared<Layer>(shared_from_this(), layerId);
// 保存當前Display全部的Layer
mLayers.emplace(layerId, layer);
// 返回建立的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);
}複製代碼
能夠經過類圖,直觀感覺下引用關係。
HWC2::Device
構造函數除了完成獲取函數指針實現之外,還會經過Device::registerCallbacks
向硬件設備註冊三個Display
的回調:熱插拔,刷新和VSync信號,以下所示:
// HWC硬件的幾種回調描述符
// 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回調
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);
}
// 模板函數,用於向硬件設備(hwc2_device_t)註冊函數回調
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);
// 把函數指針強轉成統一的void (*)() 函數指針類型
auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
// 向hwc2_device_t註冊函數回調,callbackData表示透傳的資源
mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
}
// 以VSync的靜態函數爲例
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獲取對應的HWC2::Display
auto display = device->getDisplayById(displayId);
if (display) {
// 向外回調
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可調用對象mVsync向外回調,該可調用對象實際是HWComposer經過Device::registerVsyncCallback方法註冊的
if (mVsync) {
mVsync(std::move(display), timestamp);
} else {
mPendingVsyncs.emplace_back(std::move(display), timestamp);
}
} 複製代碼
總結一下,HWC2::Device
構造函數向硬件設備註冊三個Display
回調:熱插拔,刷新和VSync信號。當HWC2::Device
收到這些回調時,會經過監聽器向外回調到對應的HWComposer函數:HWComposer::hotplug
/HWComposer::invalidate
/HWComposer::vsync
。HWComposer再經過這些信息驅動對應工做,後續文章進行介紹。
上文提到HWC2::Device
中的函數指針是hardwarelibhardwareincludehardwarehwcomposer2.h中定義的,除此以外,該頭文件還定義了一些重要的結構體,這裏介紹兩個比較重要的:
// 顯示屏類型
enum class DisplayType : int32_t {
Invalid = HWC2_DISPLAY_TYPE_INVALID,
// 物理顯示屏,顯示設備有一個主屏幕,而後能夠經過熱插拔添加或者刪除外接顯示屏
Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
// 虛擬顯示屏,內容會渲染到離屏緩衝區,Android錄屏功能就是基於虛擬屏實現的
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
表示顯示屏類型,上面註釋已經介紹,重點看下Layer合成類型:
Gralloc
模塊分配),而後再經過Display::setClientTarget
把這塊圖形Buffer的地址傳遞給HWC設備,最後由HWC設備把其餘Layer和這個圖形Buffer進一步合成,並最終展現在Display上。 SurfaceFlinger
會配置每一個Layer都經過Device
方式合成,可是HWC設備會根據硬件設備的性能改變某些圖層的合成方式。 Layer::setColor
設置的顏色渲染這個圖層,若是HWC設備不支持這種合成方式,那麼將會請求SurfaceFlinger
改變合成方式爲Client。 SurfaceFlinger
改變合成方式爲Client或者Device。 HWC2_CAPABILITY_SIDEBAND_STREAM
能力的設備才支持這種圖層,若設備不支持,那麼將會請求SurfaceFlinger
改變合成方式爲Client或者Device。 那麼一個Layer
的合成方式是怎麼肯定的那?大體流程以下所示:
validateDisplay
,讓HWC決定每一個Layer的合成方式。 getChangedCompositionTypes
檢查HWC是否對任何Layer的合成方式作出了改變,如果,那麼SurfaceFlinger則調整對應Layer的合成方式,而且調用acceptDisplayChanges
通知HWC。 Client
類型的Layer合成到Target圖形緩衝區,而後調用setClientTarget
把Target Buffer設置給HWC。(若是沒有Client類型的Layer,則能夠跳過該方法) presentDisplay
,讓HWC完成剩餘Layer的合成,而且在顯示屏上展現出最終的合成結果。 本篇文章只是簡單介紹了HWC模塊的相關類:HWComposer
、HWC2::Device
、HWC2::Display
和HWC2::Layer
,以及它們的關係。此外,還着重介紹了Layer的合成方式和合成流程。後續文章會更加全面的介紹SurfaceFlinger
是如何經過HWC模塊完成Layer合成和上屏的(虛擬屏幕是到離屏緩衝區)。