HWC 概述
HWC
(hwcomposer)是 Android 中进行窗口(Layer
)合成和显示的 HAL 层模块,其实现是特定于设备的,而且通常由显示设备制造商 (OEM)完成,为 SurfaceFlinger
服务提供硬件支持。
SurfaceFlinger
可以使用 OpenGL ES
合成 Layer
,这需要占用并消耗 GPU 资源。大多数 GPU 都没有针对图层合成进行优化,当SurfaceFlinger
通过 GPU 合成图层时,应用程序无法使用 GPU 进行自己的渲染。而 HWC
通过硬件设备进行图层合成,可以减轻 GPU 的合成压力。
显示设备的能力千差万别,很难直接用 API 表示硬件设备支持合成的 Layer
数量,Layer
是否可以进行旋转和混合模式操作,以及对图层定位和硬件合成的限制等。因此 HWC 描述上述信息的流程是这样的:
-
SurfaceFlinger
向HWC
提供所有Layer
的完整列表,让HWC
根据其硬件能力,决定如何处理这些Layer
。 -
HWC
会为每个Layer
标注合成方式,是通过 GPU 还是通过HWC
合成。 -
SurfaceFlinger
负责先把所有注明 GPU 合成的Layer
合成到一个输出 Buffer,然后把这个输出 Buffer 和其他Layer
(注明 HWC 合成的 Layer)一起交给HWC
,让HWC
完成剩余Layer
的合成和显示。
虽然每个显示设备的能力不同,但是官方要求每个 HWC
硬件模块都应该支持以下能力:
- 至少支持 4 个叠加层:状态栏、系统栏、应用本身和壁纸或者背景。
- 叠加层可以大于显示屏,例如:壁纸
- 同时支持预乘每像素(per-pixel)Alpha 混合和每平面(per-plane)Alpha 混合。
- 为了支持受保护的内容,必须提供受保护视频播放的硬件路径。
- RGBA packing order, YUV formats, and tiling, swizzling, and stride properties
Tiling:可以把 Image 切割成 MxN 个小块,最后渲染时,再将这些小块拼接起来,就像铺瓷砖一样。
Swizzling:一种拌和技术,表示向量单元可以被任意地重排或重复。
但是并非所有情况下 HWC
都比 GPU 更高效,例如:当屏幕上没有任何变化时,尤其是叠加层有透明像素并且需要进行图层透明像素混合时。在这种情况下,HWC
可以要求部分或者全部叠加层都进行 GPU 合成,然后 HWC
持有合成的结果 Buffer,如果 SurfaceFlinger
要求合成相同的叠加图层列表,HWC
可以直接显示之前合成的结果 Buffer,这有助于提高待机设备的电池寿命。
HWC
也提供了 VSync
事件,用于管理渲染和图层合成时机,后续文章会进行介绍。
HWC2 实现
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
构造函数通过 loadHwcModule
方法加载 HWC 模块,并封装成 HWC2::Device
结构,如下所示:
// 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;
}
}
这些函数指针主要分为三类:
- 硬件设备(Device)相关的函数指针
- 显示屏幕(Display)相关的函数指针
- 叠加图层(Layer)相关的函数指针
通过上述函数指针可以与 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 再通过这些信息驱动对应工作,后续文章进行介绍。
HWC Layer 合成方式
上文提到 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 合成类型:
- Client:这里的 Client 是相对于 HWC 硬件设备来说的,即不通过 HWC 来合成图层,而是通过 GPU 先把所有的这类图层合成到 client target buffer(一个离屏的图形缓冲区,buffer_handle_t 表示指向这块显存的指针,显存由
Gralloc
模块分配),然后再通过Display::setClientTarget
把这块图形 Buffer 的地址传递给 HWC 设备,最后由 HWC 设备把其他 Layer 和这个图形 Buffer 进一步合成,并最终展示在 Display 上。 - Device:通过 HWC 硬件来合成图层,默认情况下,
SurfaceFlinger
会配置每个 Layer 都通过Device
方式合成,但是 HWC 设备会根据硬件设备的性能改变某些图层的合成方式。 - SolidColor:HWC 设备将通过
Layer::setColor
设置的颜色渲染这个图层,如果 HWC 设备不支持这种合成方式,那么将会请求SurfaceFlinger
改变合成方式为 Client。 - Cursor:与 Device 类似,但是这个图层的位置可以通过 setCursorPosition 异步设置。如果 HWC 设备不支持这种合成方式,那么将会请求
SurfaceFlinger
改变合成方式为 Client 或者 Device。 - Sideband:HWC 硬件会处理该类图层的合成,以及它的缓冲区更新和内容同步,但是只有拥有
HWC2_CAPABILITY_SIDEBAND_STREAM
能力的设备才支持这种图层,若设备不支持,那么将会请求SurfaceFlinger
改变合成方式为 Client 或者 Device。
那么一个 Layer
的合成方式是怎么确定的那?大致流程如下所示:
- 当 VSync 信号到来时,SurfaceFlinger 被唤醒,处理 Layer 的新建,销毁和更新,并且为相应 Layer 设置期望的合成方式。
- 所有 Layer 更新后,SurfaceFlinger 调用
validateDisplay
,让 HWC 决定每个 Layer 的合成方式。 - SurfaceFlinger 调用
getChangedCompositionTypes
检查 HWC 是否对任何 Layer 的合成方式做出了改变,若是,那么 SurfaceFlinger 则调整对应 Layer 的合成方式,并且调用acceptDisplayChanges
通知 HWC。 - SurfaceFlinger 把所有
Client
类型的 Layer 合成到 Target 图形缓冲区,然后调用setClientTarget
把 Target Buffer 设置给 HWC。(如果没有 Client 类型的 Layer,则可以跳过该方法) - 最后,SurfaceFlinger 调用
presentDisplay
,让 HWC 完成剩余 Layer 的合成,并且在显示屏上展示出最终的合成结果。
总结
本篇文章只是简单介绍了 HWC 模块的相关类:HWComposer
、HWC2::Device
、HWC2::Display
和 HWC2::Layer
,以及它们的关系。此外,还着重介绍了 Layer 的合成方式和合成流程。后续文章会更加全面的介绍SurfaceFlinger
是如何通过 HWC 模块完成 Layer 合成和上屏的(虚拟屏幕是到离屏缓冲区)。
相关源码
- hardwarelibhardwareincludehardwarehardware.h
- hardwarelibhardwarehardware.c
- hardwarelibhardwareincludehardwarehwcomposer_defs.h
- hardwarelibhardwareincludehardwarehwcomposer.h
- hardwarelibhardwareincludehardwarehwcomposer2.h
- frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.h
- frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.cpp
- frameworksnativeservicessurfaceflingerDisplayHardwareHWComposer.h
- frameworksnativeservicessurfaceflingerDisplayHardwareHWComposer.cpp
- frameworksnativeservicessurfaceflingerSurfaceFlinger.h
- frameworksnativeservicessurfaceflingerSurfaceFlinger.cpp