Android圖形系統系統篇之HWC

HWC概述

HWC(hwcomposer)是Android中進行窗口(Layer)合成和顯示的HAL層模塊,其實現是特定於設備的,並且一般由顯示設備製造商 (OEM)完成,爲SurfaceFlinger服務提供硬件支持。android

SurfaceFlinger可使用OpenGL ES合成Layer,這須要佔用並消耗GPU資源。大多數GPU都沒有針對圖層合成進行優化,當SurfaceFlinger經過GPU合成圖層時,應用程序沒法使用GPU進行本身的渲染。而HWC經過硬件設備進行圖層合成,能夠減輕GPU的合成壓力。數據結構

顯示設備的能力千差萬別,很難直接用API表示硬件設備支持合成的Layer數量,Layer是否能夠進行旋轉和混合模式操做,以及對圖層定位和硬件合成的限制等。所以HWC描述上述信息的流程是這樣的:composer

  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個小塊,最後渲染時,再將這些小塊拼接起來,就像鋪瓷磚同樣。ide

Swizzling:一種拌和技術,表示向量單元能夠被任意地重排或重複。函數

可是並不是全部狀況下HWC都比GPU更高效,例如:當屏幕上沒有任何變化時,尤爲是疊加層有透明像素而且須要進行圖層透明像素混合時。在這種狀況下,HWC能夠要求部分或者所有疊加層都進行GPU合成,而後HWC持有合成的結果Buffer,若是SurfaceFlinger要求合成相同的疊加圖層列表,HWC能夠直接顯示以前合成的結果Buffer,這有助於提升待機設備的電池壽命。性能

HWC也提供了VSync事件,用於管理渲染和圖層合成時機,後續文章會進行介紹。優化

HWC2實現

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_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
相關文章
相關標籤/搜索