本文档主要介绍图形基础适配及点屏。
环境说明:
- 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
效果如下:
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
编译过程中,旧版本可能会报如下错误: [图片上传失败…(image-441a5b-1724665679599)]
解决方案如下:
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;
}
最终显示效果如下:
运行 hello_composer 能出现以上界面,代码图形 HDI 基础适配完成
三、CPU 点屏
Display HDI 能正常出图后,图形这一块就准备妥当了,这个时候你只需要重新启动系统,盯着屏幕看,能看到开机动画,并进入如下界面,就恭喜你,可以把网页擦掉,后面不用看了。
如果没有启动,那就按下面的步骤排查一下:
-
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.
解决方案:合入以下 patchhttps://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/
写在最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(OpenHarmony)资料用来跟着学习是非常有必要的。
想更深入的学习 OpenHarmony (鸿蒙南向)全栈开发的内容可点击领取一下学习文档,限时开源,先到先得~无套路领取!!
请点击→《鸿蒙南向开发学习路线》
OpenHarmony入门学习视频
鸿蒙南向开发实战教学视频
获取以上完整鸿蒙HarmonyOS学习文档,请点击→《鸿蒙南向开发学习路线》
《OpenHarmony源码解析》:
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
系统架构分析:
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……