1. 环境

系统:OpenHarmony 5.0.1 Release

单板:rk3568

2. 框架图

基于主流的DRM显示框架,提供一套display hdi的默认参考实现,支持DRM架构通过hdi对接ohos图形服务,对上层图形服务提供HDI驱动操作能力,主要包括显示设备管理、图层合成、显示内存管理等功能。

Display HDI接口能力主体分为三类:

  • display_gralloc:显示内存相关的hdi操作接口,例如显示内存申请,内存映射等;
  • display_device:显示设备操作、图层设置以及合成相关的hdi接口,例如图层的创建、图层的各种设置、图层预处理以及提交合成等接口;涉及显示设备的初始化流程、刷图流程等。
  • display_gfx:显示2D加速的相关接口,此接口实现与各芯片厂商的实现和逻辑设计有关,不提供统一参考,可参考RK3568的相关接口。

3. display_buffer的HDI层

3.1 架构图

img

3.2 interface实现

3.2.1 IDisplayBuffer接口

class IDisplayBuffer {
public:
    virtual ~IDisplayBuffer() = default;
    virtual bool AddDeathRecipient(const sptr<IRemoteObject::DeathRecipient>& recipient) = 0;
    virtual bool RemoveDeathRecipient() = 0;

    static IDisplayBuffer *Get();
    virtual int32_t AllocMem(const AllocInfo& info, BufferHandle*& handle) const = 0;
    virtual void FreeMem(const BufferHandle& handle) const = 0;
    virtual void *Mmap(const BufferHandle& handle) const = 0;
    virtual int32_t Unmap(const BufferHandle& handle) const = 0;
    virtual int32_t FlushCache(const BufferHandle& handle) const = 0;
    virtual int32_t InvalidateCache(const BufferHandle& handle) const = 0;
    virtual int32_t IsSupportedAlloc(
        const std::vector<VerifyAllocInfo>& infos, std::vector<bool>& supporteds) const = 0;
};

Surface接口调用IDisplayBuffer接口。

IDisplayBuffer在框架层通过DisplayBufferHdiImpl类实现。

DisplayBufferHdiImpl实现类通过Proxy/Stub机制或者Passthrough机制调用IDisplayBufferVdi接口。

3.2.2 编译生成

3.2.2.1 目录结构

//drivers/interface/display/buffer

├── v1_0
│   ├── BUILD.gn
│   ├── DisplayBufferType.idl
│   ├── IAllocator.idl
│   ├── IMapper.idl
│   ├── hdi_impl
│   │   ├── display_buffer_hdi_impl.cpp
│   │   └── display_buffer_hdi_impl.h
│   └── include
│       └── idisplay_buffer.h
├── v1_1
│   ├── BUILD.gn
│   ├── IMetadata.idl
│   ├── hdi_impl
│   │   ├── display_buffer_hdi_impl.cpp
│   │   └── display_buffer_hdi_impl.h
│   └── include
│       └── idisplay_buffer.h
└── v1_2
    ├── BUILD.gn
    ├── DisplayBufferType.idl
    ├── IMapper.idl
    ├── hdi_impl
    │   ├── display_buffer_hdi_impl.cpp
    │   └── display_buffer_hdi_impl.h
    └── include
        └── idisplay_buffer.h

bundle.json

//drivers/interface/display/bundle.json
  "//drivers/interface/display/buffer/v1_0:display_buffer_idl_target",
  "//drivers/interface/display/buffer/v1_0:libdisplay_buffer_hdi_impl",
  "//drivers/interface/display/buffer/v1_1:display_buffer_idl_target",
  "//drivers/interface/display/buffer/v1_1:libdisplay_buffer_hdi_impl_v1_1",
  "//drivers/interface/display/buffer/v1_2:display_buffer_idl_target",
  "//drivers/interface/display/buffer/v1_2:libdisplay_buffer_hdi_impl_v1_2",
3.2.2.2 编译idl

display_buffer提供4个idl文件,通过编译工具链自动调用hdi-gen工具生成框架代码。

IAllocator.idl
IMapper.idl
DisplayBufferType.idl
IMetadata.idl

drivers/interface/display/buffer/v1_2/BUILD.gn

hdi("display_buffer") {
  module_name = "display_buffer"
  imports = [ "ohos.hdi.display.buffer.v1_0:display_buffer" ]

  sources = [
    "DisplayBufferType.idl",
    "IMapper.idl",
  ]
  innerapi_tags = [
    "chipsetsdk_indirect",
    "platformsdk_indirect",
  ]
  language = "cpp"
  subsystem_name = "hdf"
  part_name = "drivers_interface_display"
}

编译命令

./build.sh --product-name rk3568 –ccache --gn-flags="--export-compile-commands" -T drivers/interface/display/buffer/v1_2:display_buffer_idl_target

生成模版源代码

//out/rk3568/gen/drivers/interface/display/buffer
├── v1_0
│   ├── allocator_driver.cpp
│   ├── allocator_proxy.cpp
│   ├── allocator_proxy.h
│   ├── allocator_service.cpp
│   ├── allocator_service.h
│   ├── allocator_stub.cpp
│   ├── allocator_stub.h
│   ├── display_buffer_type.cpp
│   ├── display_buffer_type.h
│   ├── iallocator.h
│   ├── imapper.h
│   ├── mapper_driver.cpp
│   ├── mapper_proxy.cpp
│   ├── mapper_proxy.h
│   ├── mapper_service.cpp
│   ├── mapper_service.h
│   ├── mapper_stub.cpp
│   └── mapper_stub.h
└── v1_2
    ├── display_buffer_type.cpp
    ├── display_buffer_type.h
    ├── imapper.h
    ├── mapper_driver.cpp
    ├── mapper_proxy.cpp
    ├── mapper_proxy.h
    ├── mapper_service.cpp
    ├── mapper_service.h
    ├── mapper_stub.cpp
    └── mapper_stub.h

生成so文件

//out/rk3568/hdf/drivers_interface_display
├── libdisplay_buffer_proxy_1.0.z.so
├── libdisplay_buffer_proxy_1.2.z.so
├── libdisplay_buffer_stub_1.0.z.so
└── libdisplay_buffer_stub_1.2.z.so
3.2.2.3 编译生成hdi_impl

drivers/interface/display/buffer/v1_2/BUILD.gn

ohos_shared_library("libdisplay_buffer_hdi_impl_v1_2") {
  sources = [
    "../v1_0/hdi_impl/display_buffer_hdi_impl.cpp",
    "../v1_1/hdi_impl/display_buffer_hdi_impl.cpp",
    "./hdi_impl/display_buffer_hdi_impl.cpp",
  ]

  public_configs = [ ":libdisplay_buffer_hdi_impl_config" ]

  deps = [
    ":libdisplay_buffer_proxy_1.2",
    "../v1_0:libdisplay_buffer_proxy_1.0",
    "../v1_1:libdisplay_buffer_proxy_1.1",
  ]
  # ......
}

编译命令

./build.sh --product-name rk3568 –ccache --gn-flags="--export-compile-commands" -T drivers/interface/display/buffer/v1_2:libdisplay_buffer_hdi_impl_v1_2

生成so文件

//out/rk3568/hdf/drivers_interface_display
├── libdisplay_buffer_hdi_impl_v1_2.z.so
├── libdisplay_buffer_proxy_1.0.z.so
├── libdisplay_buffer_proxy_1.1.z.so
├── libdisplay_buffer_proxy_1.2.z.so
├── libdisplay_buffer_stub_1.0.z.so
└── libdisplay_buffer_stub_1.2.z.so

AllocMem内存申请接口使用IPC模式跨进程调用,实现进程隔离,满足安全要求。

FreeMem、Mmap、UnMap等接口使用Passthrough直通模式,提升性能。因map、free等接口涉及访问的内核节点安全权限要求低,甚至不涉及内核节点,而且map接口返回的是虚拟地址,无发跨进程调用。

3.3 service实现

3.3.1 编译生成

框架层已经实现allocator_host的相关流程。

host驱动通过stub机制调用AllocatorService,AllocatorService调用IDisplayBufferVdi接口,响应Surface的调用过程。

源码结构:

//drivers/peripheral/display/buffer/hdi_service
├── BUILD.gn
├── include
│   ├── allocator_service.h
│   ├── idisplay_buffer_vdi.h
│   ├── mapper_service.h
│   ├── mapper_service_1_2.h
│   └── metadata_service.h
└── src
    ├── allocator_driver.cpp
    ├── allocator_service.cpp
    ├── mapper_driver.cpp
    ├── mapper_service.cpp
    ├── mapper_service_1_2.cpp
    ├── metadata_driver.cpp
    └── metadata_service.cpp

BUILD.gn

//drivers/peripheral/display/buffer/hdi_service/BUILD.gn
group("hdf_display_buffer_service") {
  deps = [
    ":liballocator_driver_1.0",
    ":liballocator_service_1.0",
    ":libmapper_driver_1.0",
    ":libmapper_service_1.0",
    ":libmapper_service_1.2",
    ":libmetadata_driver_1.1",
    ":libmetadata_service_1.1",
  ]
}

编译命令

./build.sh --product-name rk3568 –ccache --gn-flags="--export-compile-commands" -T hdf_display_buffer_service

生成so文件

//out/rk3568/hdf/drivers_peripheral_display
liballocator_driver_1.0.z.so
libmapper_driver_1.0.z.so
libmapper_service_1.2.z.so
libmetadata_service_1.1.z.so
liballocator_service_1.0.z.so
libmapper_service_1.0.z.so
libmetadata_driver_1.1.z.so 

3.3.2 配置hcs

通过isStub参数判断是否是Proxy/Stub还是Passthrough模式。

IMapper和IMetadata是直通模式,运行时序图参考3.2.2,直接加载libdisplay_buffer_vdi_impl.z.so。

IAllocator是Proxy/Stub模式,通过IPC和allocator_host进程通信。

参考rk3568的hcs配置allocator_host进程

vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs

allocator :: host {
    hostName = "allocator_host";
    priority = 40;
    allocator_device :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 160;
            moduleName = "liballocator_driver_1.0.z.so";
            serviceName = "allocator_service";
        }
    }
}

4. VDI适配层

4.1 drm介绍

OpenHarmony基于主流的DRM显示框架,提供一套VDI接口的默认参考实现。DRM是Linux目前主流的图形显示框架,可以统一管理GPU和Display驱动,使得软件架构更为统一,方便管理和维护。

DRM从模块上划分,可以简单分为3部分:libdrm、KMS、GEM

  • libdrm
    对底层接口进行封装,向上层提供通用的API接口,主要是对各种IOCTL接口进行封装。

  • KMS
    Kernel Mode Setting,所谓Mode setting,其实说白了就两件事:更新画面和设置显示参数。
    更新画面:显示buffer的切换,多图层的合成方式,以及每个图层的显示位置。
    设置显示参数:包括分辨率、刷新率、电源状态(休眠唤醒)等。

  • GEM
    Graphic Execution Manager,主要负责显示buffer的分配和释放,也是GPU唯一用到DRM的地方。

  • 基本元素
    DRM框架涉及到的元素很多,大致如下:
    KMS:CRTC,ENCODER,CONNECTOR,PLANE,FB,VBLANK,property
    GEM:DUMB、PRIME、fence

详情参考:DRM(Direct Rendering Manager)学习简介-CSDN博客

OpenHarmony VDI接口参考实现的源码结构:

drivers/peripheral/display

├── BUILD.gn
├── include
│   ├── display_adapter_interface.h
│   ├── display_common.h
│   ├── display_gfx.h                                # 2D加速接口
│   └── display_gralloc_private.h
└── src
    ├── display_device
    │   ├── composer                                # 图层合成管理
    │   ├── core                                    # 显示设备和图层设置
    │   ├── display_composer_vdi_impl.cpp
    │   ├── display_composer_vdi_impl.h
    │   ├── drm                                        # drm设备
    │   ├── fbdev                                    # FrameBuffer设备
    │   └── vsync                                    # softvsync
    ├── display_gfx                                    # 2D加速实现
    │   └── display_gfx.cpp
    ├── display_gralloc
    │   ├── allocator.cpp
    │   ├── allocator.h
    │   ├── allocator_manager.cpp
    │   ├── allocator_manager.h
    │   ├── display_buffer_vdi_impl.cpp
    │   ├── display_buffer_vdi_impl.h
    │   ├── dmabufferheap_allocator.cpp                # dmabufferheap管理
    │   ├── dmabufferheap_allocator.h
    │   ├── drm_allocator.cpp                        # drm gem显示内存管理
    │   ├── drm_allocator.h
    │   ├── framebuffer_allocator.cpp                # framebuffer管理
    │   └── framebuffer_allocator.h
    ├── display_layer_video
    │   └── display_layer_video.cpp                    # video图层操作
    └── utils
        ├── display_adapter.cpp                        # fb设备桥接
        ├── display_adapter.h
        └── display_module_loader.h

4.2 IDisplayBufferVdi的实现

4.2.1 接口

IDisplayBufferVdi接口:

drivers/peripheral/display/buffer/hdi_service/include/idisplay_buffer_vdi.h

#define DISPLAY_BUFFER_VDI_LIBRARY "libdisplay_buffer_vdi_impl.z.so"

class IDisplayBufferVdi {
public:
    virtual ~IDisplayBufferVdi() = default;
    virtual int32_t AllocMem(const AllocInfo& info, BufferHandle*& handle) const = 0;
    virtual void FreeMem(const BufferHandle& handle) const = 0;
    virtual void *Mmap(const BufferHandle& handle) const = 0;
    virtual int32_t Unmap(const BufferHandle& handle) const = 0;
    virtual int32_t FlushCache(const BufferHandle& handle) const = 0;
    virtual int32_t InvalidateCache(const BufferHandle& handle) const = 0;
    virtual int32_t IsSupportedAlloc(
        const std::vector<VerifyAllocInfo>& infos, std::vector<bool>& supporteds) const = 0;
    virtual int32_t RegisterBuffer(const BufferHandle& handle)
    {
        return 0;
    }
    virtual int32_t SetMetadata(const BufferHandle& handle, uint32_t key, const std::vector<uint8_t>& value)
    {
        return 0;
    }
    virtual int32_t GetMetadata(const BufferHandle& handle, uint32_t key, std::vector<uint8_t>& value)
    {
        return 0;
    }
    virtual int32_t ListMetadataKeys(const BufferHandle& handle, std::vector<uint32_t>& keys)
    {
        return 0;
    }

    virtual int32_t EraseMetadataKey(const BufferHandle& handle, uint32_t key)
    {
        return 0;
    }
    virtual int32_t GetImageLayout(const BufferHandle& handle, V1_2::ImageLayout& layout) const
    {
        return 0;
    }
};

using CreateDisplayBufferVdiFunc = IDisplayBufferVdi* (*)();
using DestroyDisplayBufferVdiFunc = void (*)(IDisplayBufferVdi* vdi);
extern "C" IDisplayBufferVdi* CreateDisplayBufferVdi();
extern "C" void DestroyDisplayBufferVdi(IDisplayBufferVdi* vdi);

4.2.2 实现

IDisplayBufferVdi接口的实现:

参考drivers/peripheral/display/hal/default_standard/src/display_gralloc标准实现,编译生成libdisplay_buffer_vdi_impl.z.so

libdisplay_buffer_vdi_impl的gn文件示例,参考rk3568

# device/soc/rockchip/rk3568/hardware/display/BUILD.gn
ohos_shared_library("libdisplay_buffer_vdi_impl") {
  sources = [ "src/display_gralloc/display_buffer_vdi_impl.cpp" ]

  public_configs = [ ":libdisplay_buffer_vdi_impl_config" ]

  include_dirs = [
    "./src/display_gralloc",
    "${root_path}/drivers/peripheral/base",
    "${root_path}/drivers/interface/display/composer/hdifd_parcelable",
    "${root_path}/drivers/interface/display/buffer",
    "${root_path}/drivers/peripheral/display/utils/include",
    "${root_path}/drivers/peripheral/display/buffer/hdi_service/include",
  ]

  output_name = "libdisplay_buffer_vdi_impl"
  cflags = [
    "-DGRALLOC_GBM_SUPPORT",
    "-Wno-macro-redefined",
  ]
  deps = [ ":libdisplay_buffer_vendor" ]

  external_deps = [
    "c_utils:utils",
    "drivers_interface_display:display_buffer_idl_headers",
    "drivers_interface_display:display_composer_idl_headers",
    "hdf_core:libhdf_utils",
    "hilog:libhilog",
    "ipc:ipc_single",
  ]

  install_enable = true
  install_images = [ chipset_base_dir ]
  innerapi_tags = [ "passthrough" ]
  subsystem_name = "hdf"
  part_name = "rockchip_products"
}

参考实现已经实现IDisplayBufferVdi的相关流程,只需继承Allocator类实现Allocate()函数即可。

img

当前参考实现提供了FramebufferAllocator、DrmAllocator、DmaBufferHeadAllocator等实现,可以通过继承Allocator类并实现Allocate()函数,适配新的Allocator。

显示内存数据结构BufferHandle:

typedef struct {
    int32_t fd;           /**< buffer fd, -1 if not supported */
    int32_t width;        /**< the width of memory */
    int32_t stride;       /**< the stride of memory */
    int32_t height;       /**< the height of memory */
    int32_t size;         /* < size of memory */
    int32_t format;       /**< the format of memory */
    uint64_t usage;        /**< the usage of memory */
    void *virAddr;        /**< Virtual address of memory  */
    uint64_t phyAddr;     /**< Physical address */
    uint32_t reserveFds;  /**< the number of reserved fd value */
    uint32_t reserveInts; /**< the number of reserved integer value */
    int32_t reserve[0];   /**< the data */
} BufferHandle;

DrmAllocator的参考实现如下:

int32_t DrmAllocator::Allocate(const BufferInfo &bufferInfo, BufferHandle &handle)
{
    // 打开DRM设备文件
    drmFd_ = open("/dev/dri/card0", O_RDWR);
    
    // 用于放弃对DRM设备的主控权。在DRM中,主控权是指对设备的完全控制,包括模式设置、输出配置等。通常,只有拥有主控权的进程才能进行这些操作。
    drmDropMaster(drmFd_);
    
    // 创建DUMB buffer对象
    // DUMB buffer只支持连续物理内存,基于kernel中通用的CMA API实现。
    // Prime buffer是DRM中跨设备缓冲区的共享框架,基于dma-buf实现,可以是连续物理内存,也可以是离散的物理内存。
    // DUMB buffer可以通过PRIME机制在不同进程中共享。
    drmIoctl(drmFd_, DRM_IOCTL_MODE_CREATE_DUMB, &dumb);
    
    // 将DRM PRIME handle 转换为文件描述符fd,可在不同的进程之间使用fd标识共享显示内存。
    drmPrimeHandleToFD(drmFd_, dumb.handle, DRM_CLOEXEC | DRM_RDWR, &fd);
    
    // hisi平台获取显示内存的物理地址
    ioctl(drmFd_, DRM_IOCTL_HISILICON_GEM_FD_TO_PHYADDR, &args);
    
    // 销毁DUMB buffer对象
    drmIoctl(drmFd_, DRM_IOCTL_MODE_DESTROY_DUMB, &destoryDumb)
}
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐