searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

安卓模拟器图像合成介绍

2023-02-08 02:10:26
150
0

0 前言

    安卓系统的每帧画面其实都是由若干图层合并而成的,该操作称之为“图像合成”[3]:

图0.1 图像合成示意图[3]

    本文将对合成方式和它们的性能对比进行说明。

1 安卓

    安卓系统主要包含GPU和HWC两种合成方式,可通过开发者模式关闭和打开HWC合成:

图1.1

 

    也可通过下面命令禁止/允许HWC合成:

adb shell service call SurfaceFlinger 1008 i32 1 // 禁止HWC合成
adb shell service call SurfaceFlinger 1008 i32 0 // 允许HWC合成

    在禁止HWC合成的情况下只能走GPU合成,那么在否允许HWC合成的情况下是否就一定走HWC合成呢?答案是否定的,其影响因素有多个,其中最为主要的就是HWC硬件模块是否支持对该图层进行硬件合成。安卓模拟器直到29.0版本才模拟实现HWC硬件模块,同时安卓10的HWC HAL层提供了相应的软件支持,因此原生系统至少需要"模拟器29.0 + 安卓10"支持HWC合成,更早期的版本只能用GPU合成。

1.1 合成方式选择

   SurfaceFlinger在创建图层时会为其选择对应的合成方式(如果禁止HWC合成所有图层均被强制设置为GPU合成,否则多数情况下选择HWC合成),但这并非最终的结果的,它需要交HWC HAL层进行校验,如果它判断出HWC硬件模块不支持对该图层进行HWC合成,则依然会将其强制改为GPU合成。

(1)安卓9

    安卓9模拟器的HWC在对图层进行准备(校验)时,强制将所有图层的合成方式改为HWC_FRAMEBUFFER(对应的就是GPU合成)

static int hwc_prepare(hwc_composer_device_1_t* dev __unused, size_t numDisplays, hwc_display_contents_1_t** displays) {
    ...
    for (size_t i = 0; i < contents->numHwLayers; i++) {
    // We do not handle any layers, so set composition type of any non
    // HWC_FRAMEBUFFER_TARGET layer to to HWC_FRAMEBUFFER.
        if (contents->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
            continue;
        }
        contents->hwLayers[i].compositionType = HWC_FRAMEBUFFER; // 瞧这里,全部改为HWC_FRAMEBUFFER合成(即GPU合成)
    }   
    return 0;
}
// @file: device/generic/goldfish/hwcomposer/hwcomposer.cpp

(2)安卓10

    安卓10模拟器的HWC在对图层进行准备(校验)时,不再这么粗暴,而是允许部分图层进行HWC合成:

Error EmuHWC2::Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests) {
    ...
    if (!mChanges) {
        ...
        bool hostCompositionV1 = rcEnc->hasHostCompositionV1();
        ...
        if (hostCompositionV1) {
            // Support Device and SolidColor, otherwise, fallback all layers to Client
            bool fallBack = false;
            for (auto& layer : mLayers) {
                ...
                // 只将以下3种类型的图层合成方式改回GPU合成,其余的保留为HWC合成
                if (layer->getCompositionType() == Composition::Client ||
                    layer->getCompositionType() == Composition::Cursor ||
                    layer->getCompositionType() == Composition::Sideband) {
                    ALOGW("%s: layer %u CompositionType %d, fallback", __FUNCTION__,  (uint32_t)layer->getId(), layer->getCompositionType());
                    fallBack = true;
                    break;
                }
            }
            ...
            if (fallBack) {
                for (auto& layer : mLayers) {
                    if (layer->getCompositionType() == Composition::Invalid) {
                        continue;
                    }
                    if (layer->getCompositionType() != Composition::Client) {
                        mChanges->addTypeChange(layer->getId(),  Composition::Client);
                    }
                }
            }
       }
       ...
}
// @file: device/generic/goldfish-opengl/system/hwc2/EmuHWC2.cpp

1.2 合成类型查看

    可通过下面命令查看其各图层的合成方式:

adb shell dumpsys SurfaceFlinger

    其结果中的“Display 0 HWC layers”部分的“Comp Type”列就是合成类型,其参数说明如下:

  • Client:GPU合成
  • Device:HWC合成

(1)安卓9

    安卓9模拟器尚未实现HWC合成,通过上面命令查看其各图层合成方式如下:

图1.1 安卓9图层合成方式

    可见其所有图层的合成方式均为Client(即GPU合成)。

(2)安卓10

    安卓10模拟器开始支持HWC合成,通过上面命令查看其各图层合成方式如下:

图1.2 安卓10图层合成方式

    可见其所有图层的合成方式均为DEVICE(即HWC合成)。

2 GPU合成

    所谓GPU合成,就是通过GPU来实现0.1各图层的合成,安卓模拟器的GPU主要有两种实现方案:

2.1 软件模拟

    所谓软件模拟即通过CPU来模拟实现OpenGL APIs,具体可以在安卓系统内部实现,也可以在安卓模拟器中实现。

(1)安卓内模拟实现OpenGL

    安卓9通过Swiftshader(源码路径:)模拟实现了OpenGL APIs,此时Swiftshader进程运行于虚拟机内,它直接消耗的是虚拟机的CPU和内存资源(当然,最终都会转化为宿主机的资源)。

 

(2)模拟器模拟实现OpenGL

    模拟器29.0支持间接的Swiftshader模式,也就是将原本运行于安卓虚拟机内部的Swiftshader迁移到模拟器中实现,它直接消耗的是宿主机的CPU和内存资源,性能更佳。

拓展:Swiftshader是有Google实现,此外还有其它OpenGL软件实现可供选择,例如LLVMpipe、Softpipe、OpenSWR等。

 

(3)对比

    方案2性能远胜于方案1,安卓10开始逐步淘汰方案1。

2.2 GPU加速

    安卓系统可将OpenGL APIs调用透传到宿主机,从而实现GPU加速。具体透传的方案有两种:

(1)模拟GPU

    安卓模拟器(其是QEMU的封装和二次开发)模式实现GPU,它内部会将具体的OpenGL APIs转发由宿主机的GPU实现:

图2.1 模拟GPU[6]

    上述方案是安卓模拟器29.0原生支持的方案,已经比较成熟。

(2)VirtIO-GPU

        通过VirtIO技术将GPU透传给虚拟机:

图2.2 VirtIO-GPU[6]

 

    原生QEMU已支持该方案,安装Ubuntu虚拟机可用该方案,Cuttlefish模拟器也支持了该方案,但安卓Goldfish模拟器尚未支持该方案。

(3)对比

    理论上方案2的性能更优于方案1,但想要用于安卓模拟器,还需不少移植工作。

3 HWC合成

    模拟器29.0内部实现了HWC合成的支持,安卓只需将多个图层交付给模拟器即可,下面是安卓模拟器图像合成的入口函数:

bool FrameBuffer::compose(uint32_t bufferSize, void* buffer, bool needPost) {
    ComposeDevice* p = (ComposeDevice*)buffer;
    AutoLock mutex(m_lock);

    switch (p->version) {
    case 1: { 
        Post composeCmd;
        composeCmd.composeVersion = 1; 
        composeCmd.composeBuffer.resize(bufferSize);
        memcpy(composeCmd.composeBuffer.data(), buffer, bufferSize);
        composeCmd.cmd = PostCmd::Compose;
        sendPostWorkerCmd(composeCmd);
        if(needPost) {
            post(p->targetHandle, false);
        }    
        return true;
    }    
    ...
}
// @file: emu-30-release/external/qemu/android/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp

    真机推荐使用该合成方式,因为它不仅性能好,功耗还低。那么在安卓模拟器中是否也是如此呢?答案是否定的,因为它最终也是调用宿主机OpenGL APIs实现合成,和GPU合成殊途同归,其性能并不一定比GPU合成好到哪里去。

参考资料

0条评论
0 / 1000
李****海
15文章数
0粉丝数
李****海
15 文章 | 0 粉丝