Android图形系统系统篇之HWC

11,650 阅读11分钟

HWC概述

HWC(hwcomposer)是Android中进行窗口(Layer)合成和显示的HAL层模块,其实现是特定于设备的,而且通常由显示设备制造商 (OEM)完成,为SurfaceFlinger服务提供硬件支持。

SurfaceFlinger可以使用OpenGL ES合成Layer,这需要占用并消耗GPU资源。大多数GPU都没有针对图层合成进行优化,当SurfaceFlinger通过GPU合成图层时,应用程序无法使用GPU进行自己的渲染。而HWC通过硬件设备进行图层合成,可以减轻GPU的合成压力。

显示设备的能力千差万别,很难直接用API表示硬件设备支持合成的Layer数量,Layer是否可以进行旋转和混合模式操作,以及对图层定位和硬件合成的限制等。因此HWC描述上述信息的流程是这样的:

  1. SurfaceFlingerHWC提供所有Layer的完整列表,让HWC根据其硬件能力,决定如何处理这些Layer
  2. HWC会为每个Layer标注合成方式,是通过GPU还是通过HWC合成。
  3. SurfaceFlinger负责先把所有注明GPU合成的Layer合成到一个输出Buffer,然后把这个输出Buffer和其他Layer(注明HWC合成的Layer)一起交给HWC,让HWC完成剩余Layer的合成和显示。

虽然每个显示设备的能力不同,但是官方要求每个HWC硬件模块都应该支持以下能力:

  1. 至少支持4个叠加层:状态栏、系统栏、应用本身和壁纸或者背景。
  2. 叠加层可以大于显示屏,例如:壁纸
  3. 同时支持预乘每像素(per-pixel)Alpha混合和每平面(per-plane)Alpha混合。
  4. 为了支持受保护的内容,必须提供受保护视频播放的硬件路径。
  5. 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_thw_module_thw_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;
    }
}

这些函数指针主要分为三类:

  1. 硬件设备(Device)相关的函数指针
  2. 显示屏幕(Display)相关的函数指针
  3. 叠加图层(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);
}

可以通过类图,直观感受下引用关系。HWC类图

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合成类型:

  1. Client:这里的Client是相对于HWC硬件设备来说的,即不通过HWC来合成图层,而是通过GPU先把所有的这类图层合成到client target buffer(一个离屏的图形缓冲区,bufferhandlet表示指向这块显存的指针,显存由Gralloc模块分配),然后再通过Display::setClientTarget把这块图形Buffer的地址传递给HWC设备,最后由HWC设备把其他Layer和这个图形Buffer进一步合成,并最终展示在Display上。
  2. Device:通过HWC硬件来合成图层,默认情况下,SurfaceFlinger会配置每个Layer都通过Device方式合成,但是HWC设备会根据硬件设备的性能改变某些图层的合成方式。
  3. SolidColor:HWC设备将通过Layer::setColor设置的颜色渲染这个图层,如果HWC设备不支持这种合成方式,那么将会请求SurfaceFlinger改变合成方式为Client。
  4. Cursor:与Device类似,但是这个图层的位置可以通过setCursorPosition异步设置。如果HWC设备不支持这种合成方式,那么将会请求SurfaceFlinger改变合成方式为Client或者Device。
  5. Sideband:HWC硬件会处理该类图层的合成,以及它的缓冲区更新和内容同步,但是只有拥有HWC2_CAPABILITY_SIDEBAND_STREAM能力的设备才支持这种图层,若设备不支持,那么将会请求SurfaceFlinger改变合成方式为Client或者Device。

那么一个Layer的合成方式是怎么确定的那?大致流程如下所示:Layer合成方式

  1. 当VSync信号到来时,SurfaceFlinger被唤醒,处理Layer的新建,销毁和更新,并且为相应Layer设置期望的合成方式。
  2. 所有Layer更新后,SurfaceFlinger调用validateDisplay,让HWC决定每个Layer的合成方式。
  3. SurfaceFlinger调用getChangedCompositionTypes检查HWC是否对任何Layer的合成方式做出了改变,若是,那么SurfaceFlinger则调整对应Layer的合成方式,并且调用acceptDisplayChanges通知HWC。
  4. SurfaceFlinger把所有Client类型的Layer合成到Target图形缓冲区,然后调用setClientTarget把Target Buffer设置给HWC。(如果没有Client类型的Layer,则可以跳过该方法)
  5. 最后,SurfaceFlinger调用presentDisplay,让HWC完成剩余Layer的合成,并且在显示屏上展示出最终的合成结果。

总结

本篇文章只是简单介绍了HWC模块的相关类:HWComposerHWC2::DeviceHWC2::DisplayHWC2::Layer,以及它们的关系。此外,还着重介绍了Layer的合成方式和合成流程。后续文章会更加全面的介绍SurfaceFlinger是如何通过HWC模块完成Layer合成和上屏的(虚拟屏幕是到离屏缓冲区)。

相关源码

  1. hardwarelibhardwareincludehardwarehardware.h
  2. hardwarelibhardwarehardware.c
  3. hardwarelibhardwareincludehardwarehwcomposer_defs.h
  4. hardwarelibhardwareincludehardwarehwcomposer.h
  5. hardwarelibhardwareincludehardwarehwcomposer2.h
  6. frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.h
  7. frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.cpp
  8. frameworksnativeservicessurfaceflingerDisplayHardwareHWComposer.h
  9. frameworksnativeservicessurfaceflingerDisplayHardwareHWComposer.cpp
  10. frameworksnativeservicessurfaceflingerSurfaceFlinger.h
  11. frameworksnativeservicessurfaceflingerSurfaceFlinger.cpp