OpenHarmony开发者论坛

标题: OpenHarmony图形HDI基础适配及点屏 [打印本页]

作者: Laval社区小助手    时间: 2023-10-31 11:42
标题: OpenHarmony图形HDI基础适配及点屏
[md]详情可点击:[OpenHarmony图形HDI基础适配及点屏](https://laval.csdn.net/64c7a6439 ... tml?login=from_csdn)

**本文档主要介绍图形基础适配及点屏。**

**环境说明:**

* **OHOS版本: 适用3.2-Beta3及以上**
* **内核版本: linux-5.10**
* **硬件环境: Dayu200-rk3568及其它硬件**

## 一、图形驱动测试

**OH现有框架主要支持两种显示框架,drm和FB。目前OH上主流采用drm框架。**

**drm驱动是显卡驱动的一种架构。相比FB架构,DRM更能适应当前日益更新的显示硬件。比如FB原生不支持多层合成,不支持VSYNC,不支持DMA-BUF,不支持异步更新,不支持fence机制等等,而这些功能DRM原生都支持。同时DRM可以统一管理GPU和Display驱动,使得软件架构更为统一,方便管理和维护。**

### 1.1 drm驱动测试

**在用户态,drm提供了libdrm库,并提供了modetest测试程序,来测试drm驱动是否完成了基础适配。**

**在OpenHarmony的第三方库libdrm自带了modetest,但默认没有参考编译,编译方法如下:**

#### 1.1.1 环境搭建

**1、 为 modetest 添加 BUILD.gn**

**third\_party\\libdrm\\tests\\modetest\\BUILD.gn:**

```
import("//build/ohos.gni")
ohos_executable("modetest") {
  sources = [
    "buffers.c",
    "cursor.c",
    "modetest.c",
  ]

  cflags = [
      "-Wno-pointer-arith",
  ]

  include_dirs = [
    "../",
    ".",
  ]

  configs = [ "//third_party/libdrm:libdrm_config" ]

  public_configs = [ "//third_party/libdrm:libdrm_public_config" ]

  deps = [
    "//third_party/libdrm:libdrm",
    "//third_party/libdrm/tests/util/:util",
  ]

  public_deps = []

  install_images = [
    "system",
    "updater",
  ]
  part_name = "graphic_standard"
  subsystem_name = "graphic"
}
```

**2、添加其它依赖:third\_party\\libdrm\\tests\\util\\BUILD.gn**

```
import("//build/ohos.gni")

ohos_static_library("util") {

  sources = [
    "format.c",
    "kms.c",
    "pattern.c",
  ]

  cflags = []

  include_dirs = [
    "../",
    ".",
  ]

  configs = [ "//third_party/libdrm:libdrm_config" ]

  public_configs = [ "//third_party/libdrm:libdrm_public_config" ]

  deps = [
    "//third_party/libdrm:libdrm",
  ]

  public_deps = []
}
```

**3、加入编译框架,添加到graphic依赖项:**

**foundation/graphic/graphic\_2d/bundle.json**

```
diff --git a/bundle.json b/bundle.json
index 755e6d2..f1bb7ef 100755
--- a/bundle.json
+++ b/bundle.json
@@ -56,6 +56,8 @@
         "group_type": {
           "base_group": [
             "//third_party/libpng:libpng",
+            "//third_party/libdrm/tests/util:util",
+            "//third_party/libdrm/tests/modetest:modetest",
             "//foundation/graphic/graphic_2d/interfaces/kits/napi:napi_packages",
             "//foundation/graphic/graphic_2d/rosen/modules/composer:libcomposer",
             "//foundation/graphic/graphic_2d/rosen/modules/composer/native_vsync:libnative_vsync",
```

**4、有个报错,提示这个未使用,注释的就好了**

```
third_party\libdrm\tests\util\pattern.c:988
    // void *mem_base = mem;
```

**5、开始编译并下载。生成的文件在/system/bin下**

#### 1.1.2 测试效果

**6、执行modetest.**

**首先把render\_service停止**

```
service_control stop render_service
```

**如果很卡,可以top一下,把CPU占用高的kill掉**

* **IOCTL方式**
  ```
  /system/bin/modetest -M rockchip -s 123@68:720x1280
  -M 指定drm驱动名称
  -s 指定屏幕信息
     123:connector id
     68: crtc id
     720x1280: 屏幕分辨率
  ```
* **atomic方式(OHOS Display HDI使用的方式)**
  ```
  modetest -M rockchip -D 0 -a -s 123@68:720x1280 -P 54@68:720x1280
  -M 指定drm驱动名称
  -s 指定屏幕信息
     123:connector id
     68: crtc id
     720x1280: 屏幕分辨率
  -P 指定plane信息
     54: 可用的plane id
     68: crtc id
     720x1280: plane大小
  ```

**由于HDI使用的是atomic方式,我们需要使用atomic方式测试drm接口成功,才能确保HDI正常使用。**

**直接运行modetest可以获取所有参数:**

```
/system/bin/modetest
    ……
    Encoders:
    id    crtc    type    possible crtcs    possible clones   
    120    0    Virtual    0x00000003    0x00000001
    122    0    TMDS    0x00000001    0x00000002
    135    85    DSI    0x00000002    0x00000004
    ……
```

**如果遍历了所有驱动后都找不到合适的驱动名称就退出了,而无法运行,就需要使用-D来指定dri的名称。**

**查看dri设备信息**

```
ls -l /dev/dri/by-path/
total 0
lrwxrwxrwx l root system 8 2017-08-07 13:34 platform-display-subsystem-card -> ../card0
lrwxrwxrwx l root system 8 2017-08-07 13:33 platform-display-subsystem-render -> ../renderD128
```

**然后我们就可以使用“-D display-subsystem”来替换“-M rockchip”。**

```
/system/bin/modetest -D 0 -a -s 123@68:720x1280 -P 54@68:720x1280
```

**效果如下:**

### ![微信图片_20230728154047]()

### 1.2 FB驱动测试

**由于FB方式是直接送显,可以通过修改显示buffer内容,比如写一个FFFFFFF的纯白数据送显,来测试FB驱动的正确性。**

## 二、图形HDI驱动移植

**图形HDI的移植有两种方式:**

**1、参考现有的设备,比如rk3568,把device/soc/rockchip/rk3568/hardware/display 整个文件夹复制到对应目录。**

**2、参考OH提供的参考代码drivers/peripheral/display/hal/default\_standard,并拷贝到device/soc/xx/xx/hardware目录下。**

**快速构建产品,推荐使用方式一,代码移植工作量小。**

**我们先分析一下OH提供的标准代码目录结构:**

```
├── display_device //包含显示设备操作接口
│   ├── composer //合成送显
│   │   ├── hdi_composer.cpp //合成送显执行
│   │   ├── hdi_composer.h
│   │   ├── hdi_gfx_composition.cpp //硬件合成接口
│   │   ├── hdi_gfx_composition.h
│   │   ├── hdi_video_composition.cpp //video专用通道处理
│   │   └── hdi_video_composition.h
│   ├── core //核心模块
│   │   ├── hdi_device_common.h
│   │   ├── hdi_device_interface.cpp
│   │   ├── hdi_device_interface.h
│   │   ├── hdi_display.cpp
│   │   ├── hdi_display.h
│   │   ├── hdi_fd.h
│   │   ├── hdi_layer.cpp
│   │   ├── hdi_layer.h
│   │   ├── hdi_netlink_monitor.cpp
│   │   ├── hdi_netlink_monitor.h
│   │   ├── hdi_session.cpp //对上提供的hdi接口
│   │   └── hdi_session.h
│   ├── drm //drm模式代码
│   │   ├── drm_connector.cpp drm基础模块-connector
│   │   ├── drm_connector.h
│   │   ├── drm_crtc.cpp drm基础模块-crtc
│   │   ├── drm_crtc.h
│   │   ├── drm_device.cpp
│   │   ├── drm_device.h
│   │   ├── drm_display.cpp
│   │   ├── drm_display.h
│   │   ├── drm_encoder.cpp drm基础模块-encoder
│   │   ├── drm_encoder.h
│   │   ├── drm_plane.cpp drm基础模块-plane
│   │   ├── drm_plane.h
│   │   ├── drm_vsync_worker.cpp //vsync同步信号处理
│   │   ├── drm_vsync_worker.h
│   │   ├── hdi_drm_composition.cpp //drm送显处理
│   │   ├── hdi_drm_composition.h
│   │   ├── hdi_drm_layer.cpp
│   │   └── hdi_drm_layer.h
│   ├── fbdev //FB模式代码
│   │   ├── fb_composition.cpp
│   │   ├── fb_composition.h
│   │   ├── fb_device.cpp
│   │   ├── fb_device.h
│   │   ├── fb_display.cpp
│   │   └── fb_display.h
│   └── vsync
│       ├── sorft_vsync.cpp
│       └── sorft_vsync.h
├── display_gfx
│   ├── display_gfx.c //gfx具体硬件合成实现
├── display_gralloc //图形显示buffer分配
│   ├── allocator.cpp
│   ├── allocator.h
│   ├── allocator_manager.cpp
│   ├── allocator_manager.h
│   ├── display_gralloc.cpp
│   ├── dmabufferheap_allocator.cpp
│   ├── dmabufferheap_allocator.h
│   ├── drm_allocator.cpp
│   ├── drm_allocator.h
│   ├── framebuffer_allocator.cpp
│   ├── framebuffer_allocator.h
│   ├── hisilicon_drm.h
│   ├── sprd_allocator.cpp
│   └── sprd_allocator.h
├── display_layer_video //video专用通道硬件适配
│   └── display_layer_video.cpp
└── utils
    ├── display_adapter.cpp
    ├── display_adapter.h
    └── display_module_loader.h
```

### 2.1 基础修改

#### 2.1.1 vsync信号修改。

**前期如果硬件帧信号不确定是否适配好,可以先使用软件帧信号**

**hardware/display/src/display\_device/drm\_vsync\_worker.cpp**

```
diff --git a/rk3568/hardware/display/src/display_device/drm_vsync_worker.cpp b/rk3568/hardware/display/src/display_device/drm_vsync_worker.cpp
index 53622c4..46f5c73 100644
--- a/rk3568/hardware/display/src/display_device/drm_vsync_worker.cpp
+++ b/rk3568/hardware/display/src/display_device/drm_vsync_worker.cpp
@@ -74,6 +74,7 @@ uint64_t DrmVsyncWorker::WaitNextVBlank(unsigned int &sq)
{
     constexpr uint64_t SEC_TO_NSEC = 1000 * 1000 * 1000;
     constexpr uint64_t USEC_TO_NSEC = 1000;
+#if(0)
     drmVBlank vblank = {
         .request =
             drmVBlankReq {
@@ -90,6 +91,13 @@ uint64_t DrmVsyncWorker::WaitNextVBlank(unsigned int &sq)
         DISPLAY_LOGE("wait vblank failed ret : %{public}d errno %{public}d", ret, errno));
     sq = vblank.reply.sequence;
     return (uint64_t)(vblank.reply.tval_sec * SEC_TO_NSEC + vblank.reply.tval_usec * USEC_TO_NSEC);
+#else
+    struct timespec current;
+    usleep(1000*15); //66.7Hz
+    sq = 1;
+    clock_gettime(CLOCK_MONOTONIC, ¤t);
+    return (uint64_t)(current.tv_sec * SEC_TO_NSEC + current.tv_nsec);
+#endif
}
```

#### 2.1.2 使用CPU渲染

**CPU渲染修改方法: 设置graphic\_standard\_feature\_ace\_enable\_gpu = false。包含在以下路径中:**

```
foundation/graphic/standard/graphic_config.gni
productdefine/common/inherit/rich.json
productdefine/common/products/ohos-arm64.json
```

**编译过程中,旧版本可能会报如下错误:** ![d0ef4d84e426f81a02d8f0deae91220]()

**解决方案如下:**

```
vendor/hihope/rk3568/config.json
"ace_engine_feature_enable_web = false"
```

**使用CPU模式能正常进入桌面,代表系统启动正常。**

#### 2.1.3 使用CPU合成

```
diff --git a/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp b/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp
index 67d3536..5591b92 100644
--- a/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp
+++ b/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp
@@ -79,7 +79,8 @@ bool HdiGfxComposition::CanHandle(HdiLayer &hdiLayer)
{
     DISPLAY_DEBUGLOG();
     (void)hdiLayer;
-    return true;
+    // return true;
+    return false;
}

int32_t HdiGfxComposition::SetLayers(std::vector<HdiLayer *> &layers, HdiLayer &clientLayer)
@@ -97,6 +98,9 @@ int32_t HdiGfxComposition::SetLayers(std::vector<HdiLayer *> &layers, HdiLayer &
             }
             mCompLayers.push_back(layer);
         }
+        else {
+            layer->SetDeviceSelect(COMPOSITION_CLIENT);
+        }
     }
     DISPLAY_DEBUGLOG("composer layers size %{public}zd", mCompLayers.size());
     return DISPLAY_SUCCESS;

```

### 2.2 Display HDI测试:

**Display HDI主要测试的点,包括:屏幕创建和管理、内存分配及释放、图层合成、图层送显。 **

**我们使用hello\_composer来测试以上功能是否正常。hello\_composer在3.2-release中默认没有参与编译,修改如下:**

**foundation/graphic/graphic\_2d/bundle.json**

```
diff --git a/bundle.json b/bundle.json
index 755e6d2..28bc995 100755
--- a/bundle.json
+++ b/bundle.json
@@ -63,6 +63,7 @@
             "//foundation/graphic/graphic_2d/rosen/samples/2d_graphics:drawing_sample_rs",
             "//foundation/graphic/graphic_2d/rosen/samples/2d_graphics:drawing_engine_sample",
             "//foundation/graphic/graphic_2d/rosen/samples/2d_graphics/test:drawing_sample",
+            "//foundation/graphic/graphic_2d/rosen/samples/composer:hello_composer",
             "//foundation/graphic/graphic_2d/rosen/modules/effect/effectChain:libeffectchain",
             "//foundation/graphic/graphic_2d/rosen/modules/effect/color_picker:color_picker",
             "//foundation/graphic/graphic_2d/rosen/modules/effect/skia_effectChain:skeffectchain",
```

**编译结果在/system/bin目录下,使用前需要先关闭render\_service服务:**

```
service_control stop render_service
```

**停止服务后如果系统卡顿,可以使用top查看,并关闭com.开头的相关应用。**

**因为render\_service已经关闭,cpu合成无法实现,为了能正常出图,我们需要手动实现一下硬件合成。**

**device/soc/rockchip/rk3568/hardware/display/src/display\_device/hdi\_gfx\_composition.cpp**

```
diff --git a/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp b/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp
index 67d3536..40c7c11 100644
--- a/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp
+++ b/rk3568/hardware/display/src/display_device/hdi_gfx_composition.cpp
@@ -82,6 +82,7 @@ bool HdiGfxComposition::CanHandle(HdiLayer &hdiLayer)
     return true;
}

+#include "string.h"
int32_t HdiGfxComposition::SetLayers(std::vector<HdiLayer *> &layers, HdiLayer &clientLayer)
{
     DISPLAY_DEBUGLOG("layers size %{public}zd", layers.size());
@@ -185,9 +186,21 @@ int32_t HdiGfxComposition::Apply(bool modeSet)
                     DISPLAY_LOGE("clear layer %{public}d failed", i));
                 break;
             case COMPOSITION_DEVICE:
-                ret = BlitLayer(*layer, *mClientLayer);
-                DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE,
-                    DISPLAY_LOGE("blit layer %{public}d failed ", i));
+                // ret = BlitLayer(*layer, *mClientLayer);
+                // DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE,
+                //     DISPLAY_LOGE("blit layer %{public}d failed ", i));
+                {
+                    char *clientBuff = (char *)mClientLayer->GetCurrentBuffer()->mHandle.virAddr;
+                    if(clientBuff) {
+                        HdiLayerBuffer *hdiLayer = layer->GetCurrentBuffer();
+                        char *layerBuff = (char *)hdiLayer->mHandle.virAddr;
+                        for(int y = 0; y < hdiLayer->GetHeight(); y++) {
+                            memcpy(&clientBuff[mClientLayer->GetCurrentBuffer()->GetStride() *
+                            (y + layer->GetLayerDisplayRect().y) + layer->GetLayerDisplayRect().x * 4],
+                            (char *)(&layerBuff[hdiLayer->GetStride() * y]), hdiLayer->GetStride());
+                        }
+                    }
+                }
                 break;
             default:
                 DISPLAY_LOGE("the gfx composition can not surpport the type %{public}d", compType);
```

**以上代码在硬件合成接口中实现了简单的图层合成,CanHandle中要返回ture来选择默认的硬件合成:**

```
bool HdiGfxComposition::CanHandle(HdiLayer &hdiLayer)
{
    DISPLAY_DEBUGLOG();
    (void)hdiLayer;
    return true;
}
```

**最终显示效果如下:**

![Screenshot from 2023-07-31 16-20-39]()

**运行hello\_composer能出现以上界面,代码图形HDI基础适配完成**

## 三、CPU点屏

**Display HDI能正常出图后,图形这一块就准备妥当了,这个时候你只需要重新启动系统,盯着屏幕看,能看到开机动画,并进入如下界面,就恭喜你,可以把网页擦掉,后面不用看了。**

![Screenshot from 2023-07-31 19-54-23]()

**如果没有启动,那就按下面的步骤排查一下:**

* **launcher 启动**
  **正常启动如下:**
  
  ```
  # ps -ef | grep com.
  root             6     2 0 19:16:50 ?     00:00:00 [kworker/0:0H-mmc_complete]
  root           191     2 0 19:16:51 ?     00:00:00 [krfcommd]
  root           198     2 0 19:16:52 ?     00:00:00 [mmc_complete]
  root           242     2 1 19:16:55 ?     00:00:00 [kworker/0:2H-mmc_complete]
  10008         1143   260 15 19:17:03 ?    00:00:03 com.ohos.systemui //必要的
  20010019      1317   260 0 19:17:05 ?     00:00:00 com.ohos.telephonydataability
  20010036      1351   260 16 19:17:06 ?    00:00:03 com.ohos.launcher //必要的
  10006         1376   260 4 19:17:07 ?     00:00:00 com.example.kikakeyboard:inputMethod
  20010027      1380   260 1 19:17:07 ?     00:00:00 com.ohos.medialibrary.medialibrarydata
  ```
* **关键进程启动**
  **需要先落盘一段开机hilog日志并分析,建议落盘时间3分钟以上:**
  
  ```
  //落盘
  hilog -w start -m none -f test -l 20M
  //设置debug级别
  hilog -b D
  //清除旧日志
  rm /data/log/hilog/*
  reboot
  //等待3分钟后停止hilog落盘
  hilog -w stop
  //打包
  tar -cvf /data/log/mylog.tar /data/log/hilog/*
  //到处日志分析
  hdc_std file recv /data/log/hilog/test.001.19700101-000047 .
  ```
  
  Service samgr(pid 2558) started
  Service appspawn(pid 2572) started
  Service foundation(pid 4288) started
  Service render\_service(pid 4284) started
  
  其它sa未启动分析:
  
  搜索NOT found service,如果超过3分钟还是没有启动成功,那这个服务就启动失败了。
* **常见错误**
  **access\_token没有适配,tokenid为0。**
  **报错:BINDER\_GET\_ACCESS\_TOKEN sender\_tokenid:671927108, first\_tokenid:0.**
  **解决方案:合入以下patch**
  
  ```
  https://gitee.com/openharmony/kernel_linux_4.19/pulls/4
  https://gitee.com/openharmony/kernel_linux_4.19/pulls/5
  ```
  
  **CONFIG-ASHMEM=y 共享内存的配置检查一下**
  
  **CONFIG-ANDROID-BINER-IPC=y IPC通信的检查一下**
  
  **SELINUX=permissive 先使用宽容策略**
  
  **节点没有权限,请添加到:base/startup/init/ueventd/etc/ueventd.config**
  
  **文件系统挂载,正常日志如下:**
  
  ```
  Run /init as init process
  Mount /dev/block/platform/soc/f9830000.emmc/by-name/system to /usr successful
  Mount /dev/block/platform/soc/f9830000.emmc/by-name/vendor to /vendor successful
  Mount /dev/block/platform/soc/f9830000.emmc/by-name/userdata to /data successful
  ```

## 四、知识分享

**快速编译指定模块,比如:libdisplay\_device.z.so**

```
./prebuilts/build-tools/linux-x86/bin/ninja -C out/rk3568 display_device
```

**快速下载推库:**

```
hdc shell mount -o rw,remount /vendor
hdc file send out/rk3568/hdf/drivers_peripheral_display/libdisplay_device.z.so /vendor/lib/
```

### 参考资料

> [https://blog.51cto.com/u\_14410880/4794778](https://blog.51cto.com/u_14410880/4794778?login=from_csdn)

[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5