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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。