内容和参考
文章内容
本文主要学习 Linux下显示功能的组成部分, 以及libdrm的使用。
从基础知识我们知道:DRM(Direct Rendering Manager,直接渲染管理器)是 Linux内核层的 显示.驱动框架(架构)。
显卡,最基本的功能就是把用户的绘图渲染后输出到显示屏上,DRM 主要是为了在软件层面实现这一目标。这里主要就包括两部分,硬件设备
、软件模块
显卡主要是由三类设备组成:
- Processing logic 指的是神秘的 GPU 模块
- Display controller 指的是 LCDC 控制器
- Hardware video acceleration 指的就是具体的显示接口 HDMI / DP / …
所以 DRM 由两个部分组成:
- 内核层:Kernel 的子系统 DRM驱动,这个子系统 对硬件 DPU 操作进行了一层框架封装,并对用户提供 open/close/ioctl接口 。
- 应用层:提供了一个 libdrm 库,libdrm 封装了一系列 API,便于APP直接使用来进行开发
本文先 学习下 如何使用libdrm库 以及 drm内核层的组成部分 ,这是当前学习的要点
相关参考
- linux DRM/KMS 测试工具 modetest、kmscude、igt-gpu-tools
- 何小龙-DRM基础系列
- libdrm-modetest原理及图显系统验证方法
- The DRM/KMS subsystem from a newbie’s point of view // brezillon-drm-kms.pdf
- Linux DRM(一)Display Server 和 Linux DRM的基本概念和特性-Younix脏羊
- 百问网-DRM/KMS framework : KMS下的Linux内核配置
- !!
基础知识
在做图形渲染的时候, 经常出现这三个词,其实都是同一个东西,只是在不同场景下称呼不一样
在d3d显示的时候,叫pitch 在用ffmpeg解码的时候,它叫linesize 在使用ffmpeg转换格式的时候,它叫stride _pitch = _ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment);
- 图像基础——像素深度bpp和像素格式
- VBLANK : 垂直消隐, 显示器扫描线完成最后一行后,需要重返左上角,这个过程叫做: vblank,也叫VBI(vertical blank interval-垂直空白间隔) , 因为扫描线变得blank-空白,以防止看到一个斜线显示在屏幕上.
- HBLANK : 水平消隐, 在将光信号转换为电信号的扫描过程中,扫描总是从 图像的左上角开始,水平向前行进,同时扫描点也以较慢的速率向下移动。当扫描点到达图像右侧边缘时,扫描点快速返回左侧,重新开始在第1行的起点下面进行第2行扫描,行与行之间的返回过程称为水平消隐。
- VSYNC : vertical synchronization, 指与显示器的帧数同步. 简单来说就是启用了vsync的渲染过程,帧数不会超过显示器的帧数, 一个同步会被执行. 同步的地方就是显示器扫描线结束最后一行扫描准备开始第一行扫描的地方.
- HSYNC : horizonal synchronization, 相比于vsync来说, 同步的单位从帧降到行,即是保证操作不是在扫描一行的中间出现,而是同步到下一行.
VSYNC与HSYNC频率与什么有关系
HSYNC=VSYNC×总共的扫描行数
————————————————————————————————————————
Camera sensor的同步信号可以简单理解为:Camera sensor向其信号接收端所发送的宣告信号。
HSYNC就是sensor在告诉接收端:在“HSYNC”的有效时段内sensor所有的信号输出属同一行。
对于VSYNC来讲,以高电平有效为例,VSYNC置高直到被拉低,这个区段sensor所输出的所有影像数据就组成一个frame。
同步信号的频率决定于pixel clock,比如一行有640个pixel,那么HSYNC的频率为:pclk/(640+dummy);VSYNC同理。
1.什么是”帧”?
在最早的电影里面,一幅静止的图像被称做一”帧(Frame)”,影片里的画面是每一秒钟有24帧,为什么是24帧,这个数字是怎么来的,因为人类眼睛的视觉暂留现象正好符合每秒24帧的标准,所以用多也没有意义还会浪费电影胶片,增加成本,所以就是24帧。
2.什么是”行”?
在我们用的传统CRT模似电视里面,一个电子束在水平方向的扫描被称之为行,或行扫描
- 图形的撕裂: 参考 撕裂(tear effect)
- 理解fence机制 : 参考 理解fence机制
libdrm安装
libDRM安装
# https://gitlab.freedesktop.org/mesa/drm
git clone https://gitlab.freedesktop.org/mesa/drm.git
https://gitee.com/mirrors/libdrm?_from=gitee_search # 国内源
sudo apt-get -y install libpciaccess-dev
sudo apt-get -y install meson
# install
cd drm
export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
rm -rf builddir ; meson builddir/
sudo ninja -C builddir/ install
测试工具
在安装完成后,会在build下生成一些常用的测试工具
(base) baiy@baiy:drm$ tree builddir/tests/
builddir/tests/
├── modetest
│ ├── modetest # 这就是常用的调试工具
基础框架
DRM显示框架由以下几部分组成:
- framebuffer : 帧缓存,是用一个视频输出设备从 包含完整的帧数据的一个内存缓冲区中来驱动一个视频显示设备。
- Planes : 硬件图层单元,
- crtc : 主要负责从 Framebuffer 中读出待显示的图像,并按照相应的格式输出给 Encoder。
- Encoder : 将内存的 pixel 像素编码(转换)为显示器接口(bp,hdmi)所需要的信号和图像编码格式.
- connector : 指的是具体外接的屏幕类型,比如HDMI/eDP/…..,然后根据链接方式Encoder
要强调理解一个基础概念:
GPU目前负责渲染,将渲染完成的图片放入framebuffer中; DPU(Display controller)和显示接口逻辑负责显示;
所以当前说的 DRM显示框架是不依赖GPU的,我们可以使用软件将一幅图片放到framebuffer中,来控制DPU的显示即可;DRM这个框架都是针对显示逻辑来说的;
我们只需要将: framebuffer+ (plane + crtc) + (encoder+connector) 三部分挂接起来,就可以显示出我们需要的图片;
其中:
- framebuffer:是 GPU/Soft 和 DPU公用的dma-buffer(共享存储), 它必须使用drm-memory API创建的内存缓冲区;
- plane + crtc: 是 display controller的工作,负责将显示图层叠加后,输出相应的时序;
- encoder+connector: 是显示解码/编码功能, 将图层的时许 根据显示器接口类型(eDP,HDMI)等转换成合适的显示码流,并传输;
planes
首先来了解一下,什么是Plane?即硬件图层。
注:这里的plane不是YUV里边的plane, 二者概念上不要混淆 : YUV图形格式中的Plane指的是图像数据在内存中的排列形式,一般Y通道占一段连续的内存块,UV通道占另一段连续的内存块,称之为YUV-2Plane (也叫YUV 2平面),属于软件层面。 而DRM中的Plane指的是Display Controller中用于多层合成的单个硬件图层模块,属于硬件层面。
Plane的历史
随着软件技术的不断更新,对硬件的性能要求越来越高,在满足功能正常使用的前提下,对功耗的要求也越来越苛刻。
本来 GPU可以处理所有图形任务,但是由于它运行时的功耗实在太高,设计者们决定将一部分简单的任务交给Display Controller去处理(比如合成),而让GPU专注于绘图(即渲染)这一主要任务,减轻GPU的负担,从而达到降低功耗提升性能的目的。于是,Plane(硬件图层单元)就诞生了。
Plane的功能
Plane是连接FB与CRTC的纽带,是内存的搬运工。
Plane的参数含义:
当 SRC_X/Y 和 CRTC_X/Y 不相等时,就实现了平移的效果;
当 SRC_W/H 和 CRTC_W/H 不相等时,就实现了缩放的效果;
当 SRC_W/H 和 FB_W/H 不相等时,就实现了裁剪的效果;
Plane的类型
在DRM框架中,Plane又分为如下3种类型:
Cursor(光标图层,一般用于PC系统,用于显示鼠标)、
Overlay(叠加图层,通常用于YUV格式的视频图层)、
Primary(主要图层,通常用于仅支持RGB格式的简单图层)。
需要注意的是,并不是所有的Display Controller都支持Plane,从前面Single-Buffer 案例中的drmModeSetCrtc函数也能看出,即使没有plane_id,屏幕也能正常显示。比如s3c2440这种ARM9 SoC,它的LCDC就没有Plane的概念。
但是DRM框架规定,任何一个CRTC,必须要有1个Primary Plane。 即使像S3C2440这种不带真实Plane硬件的Display Controller,也需要认为它的Primary Plane就是LCDC本身,因为它实现了从Framebuffer到CRTC的数据搬运工作,而这正是一个Plane最基本的功能。
crtc
阴极射线显像管上下文(Cathode Ray Tube Context), 作用是读取当前 Framebuffer 的像素数据并借助于 PLL 电路从其生成视频模式定时信号。
DRM 中 CRTC
模块主要的作用:
- 配置适合显示器的分辨率(kernel)并输出相应时序(hardware logic)
- 扫描 framebuffer 送显到一个或多个显示设备中
- 更新 framebuffer
CRTC 模块产生 vbank 信号进行场同步刷新
详情可参考: Linux DRM的基本概念和特性
DRM框架涉及到的元素很多,大致如下:
- KMS:CRTC,ENCODER,CONNECTOR,PLANE,FB,VBLANK,property
- GEM:DUMB、PRIME、fence
| CRTC | 对显示buffer进行扫描,并产生时序信号的硬件模块,通常指Display ControllerENCODER;
负责将CRTC输出的timing时序转换成外部设备所需要的信号的模块,
如HDMI转换器或DSI Controller | | —- | —- | | CONNECTOR | 连接物理显示设备的连接器,如HDMI、DisplayPort、DSI总线,
通常和Encoder驱动绑定在一起 | | PLANE | 硬件图层,有的Display硬件支持多层合成显示,但所有的Display Controller
至少要有1个plane | | FB | Framebuffer,单个图层的显示内容,唯一一个和硬件无关的基本元素 | | VBLANK | 软件和硬件的同步机制,RGB时序中的垂直消影区,软件通常使用硬件VSYNC来实现 | | property | 任何你想设置的参数,都可以做成property,是DRM驱动中最灵活、最方便的Mode setting机制 | | DUMB | 只支持连续物理内存,SoC系统基于kernel中通用CMA API实现,多用于小分辨率简单场景 | | PRIME | 连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多
用于大内存复杂场景 | | fence | buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题 |
[
](https://blog.csdn.net/hexiaolong2009/article/details/83720940)
modetest使用
usage: ./builddir/tests/modetest/modetest [-acDdefMPpsCvrw]
Query options:
-c list connectors // 显示所有的 connectors
-e list encoders // 显示所有的 encoders
-f list framebuffers // 显示所有的 framebuffers
-p list CRTCs and planes (pipes) // 显示所有的CRTCs和Planes
Test options:
-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>] // set a plane
-s <connector_id>[,<connector_id>][@<crtc_id>]:[#<mode index>]<mode>[-<vrefresh>][@<format>] // set a mode
-C test hw cursor
-v test vsynced page flipping
-r set the preferred mode for all connectors
-w <obj_id>:<prop_name>:<value> set property
-a use atomic API // -a use atomic API
-F pattern1,pattern2 specify fill patterns // 填充一种pattern
Generic options:
-d drop master after mode set
-M module use the given driver
-D device use the given device
Query options:提供查询操作,用于列举出connectors、encoders、framebuffers,CRTCs and planes,未指定参数时默认输出所有信息。
Test options:设定显示测试的参数。
Generic options:指定打开设备节点,DRM/KMS对用户层来说是一个标准的linux字符设备,其设备节点路径为/dev/dri/cardX、/dev/dri/renderX(之所有有两个设备节点这,涉及到DRM-Master 和 client相关的内容,这里可以简单的认为它们代表用一个设备)
-M和-D参数
我们使用modetest的打开的时候, 很可能出现一个设备都找不到的情况,这时我们需要判断两个标准:
root@baiy:/sys/module/drm# ls -al /dev/dri/
total 0
drwxr-xr-x 3 root root 80 Jun 12 11:06 .
drwxr-xr-x 21 root root 3960 Jun 5 06:01 ..
drwxr-xr-x 2 root root 60 Jun 4 06:41 by-path
crw-rw----+ 1 root video 226, 0 Jun 4 06:41 card0
root@baiy:/sys/module/drm# dmesg | grep "\[drm\] Initialized"
[ 4.764607] [drm] Initialized qxl 0.1.0 20120117 for 0000:00:02.0 on minor 0 # 其中qxl是drm_driver的name,
只要判断有以上打印,我们就可以使用 modetest -M qxl(和自己的一致) ……. 的操作,drm会尝试去 打开 /dev/dri/card0 ~ 15 去 获取版本信息,来和 qxl 对比。
drmOpenByName(name, type); type == DRM_NODE_PRIMARY
所以我们在虚拟机上也是可以使用: modetest -M qxl -s 33@32:1024x768@AR24 等命令来出图的。(modetest的测试代码 和 何小龙的应用代码没什么区别,所以何小龙的测试代码也可以在kvm的虚拟机中出图,modetest当然也行)。
另外,也可通过debugfs来查看
root@baiy:/sys/kernel/debug/dri/0# pwd
/sys/kernel/debug/dri/0
root@baiy:/sys/kernel/debug/dri/0# cat name
qxl dev=0000:00:02.0 unique=0000:00:02.0
-D 参数不考虑, 这个太麻烦。
-a属性
modetest -a ……设置时,primary图层也需要显示的-P进行配置
但测试中发现好像有以下问题:
使用了-a属性: set_property()->drmModeAtomicAddProperty() 此时 dev->req 是NULL,导致所有属性未配置。
后边才申请了dev->req if (dev.use_atomic) { dev.req = drmModeAtomicAlloc();… 但此时没有重新再次解析prop属性,导致atomic_set时,自己的-w配置无法生效。
尝试再 if (dev.use_atomic) { dev.req = drmModeAtomicAlloc(); 后边再次set_property() 解析一次参数,测试是正确的,
查看图层信息
connectors 在前边说了,是链接显示器得,所以 必须要链上显示器,获取EDID等信息,才可以识别出来;
先把connectors 命令写出来:
modetest -M i915 -D 0 -s 86@48:1920x1080 # 这里没有写图层,DRM会默认用一个color bar来链上CRTC
-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]
CRCT(ID=48) --> ENCODER(ID=85) --> CONNECTED(ID=86) --> DP-1HDMI
先 显示所有得connectors :
root@inno-nuc-03:~# modetest -c // 显示显示器信息所有的 connectors
trying to open device 'i915'...done
Connectors:
id encoder status name size (mm) modes encoders
86 85 connected DP-1 530x300 33 85 # !!! connector ID=86, encoder ID=85, status connected, 说明这个链接到DP-1接口上
modes:
index name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot) # !!! 支持得分辨率,刷新率
#0 1920x1080 60.00 1920 2008 2052 2200 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: preferred, driver
#1 1920x1080 74.97 1920 1968 2000 2080 1080 1083 1088 1119 174500 flags: phsync, nvsync; type: driver
#2 1920x1080 60.00 1920 2008 2052 2200 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: driver
......
props:
1 EDID:
flags: immutable blob
blobs:
value:
00ffffffffffff0010acfe414c395241
.....
2 DPMS:
flags: enum
enums: On=0 Standby=1 Suspend=2 Off=3
value: 0
5 link-status:
flags: enum
enums: Good=0 Bad=1
value: 0
6 non-desktop:
flags: immutable range
values: 0 1
value: 0
4 TILE:
flags: immutable blob
blobs:
value:
20 CRTC_ID: # !!!CRTC ID默认值是48
flags: object
value: 48
90 audio:
flags: enum
enums: force-dvi=18446744073709551614 off=18446744073709551615 auto=0 on=1
value: 0
91 Broadcast RGB:
flags: enum
enums: Automatic=0 Full=1 Limited 16:235=2
value: 0
92 max bpc:
flags: range
values: 6 12
value: 12
93 Content Protection:
flags: enum
enums: Undesired=0 Desired=1 Enabled=2
value: 0
......
再 显示所有得encoder(这里不是重点):
可以看到ID支持85,
root@inno-nuc-03:~# modetest -e
trying to open device 'i915'...done
Encoders:
id crtc type possible crtcs possible clones
85 48 TMDS 0x00000007 0x00000001
87 0 DPMST 0x00000007 0x00000002
88 0 DPMST 0x00000007 0x00000004
89 0 DPMST 0x00000007 0x00000008
94 0 TMDS 0x00000007 0x00000010
96 0 DPMST 0x00000007 0x00000020
97 0 DPMST 0x00000007 0x00000040
98 0 DPMST 0x00000007 0x00000080
root@inno-nuc-03:~# modetest -f
trying to open device 'i915'...done
Frame buffers:
id size pitch
再 显示所有得CRTCs和Planes(重点!!!):
root@inno-MS-7B89:modetest# ./modetest -p
trying to open device 'nouveau'...done
CRTCs: # !!! 这里开始显示所有支持得CRTC
id fb pos size
51 0 (0,0) (0x0)
#0 -nan 0 0 0 0 0 0 0 0 0 flags: ; type:
props:
25 DEGAMMA_LUT:
flags: blob
blobs:
value:
26 DEGAMMA_LUT_SIZE:
flags: immutable range
values: 0 4294967295
value: 256
27 CTM:
flags: blob
blobs:
value:
28 GAMMA_LUT:
flags: blob
blobs:
value:
29 GAMMA_LUT_SIZE:
flags: immutable range
values: 0 4294967295
value: 256
......
Planes: # !!! 这里开始显示所有支持得Planes
id crtc fb CRTC x,y x,y gamma size possible crtcs
41 0 0 0,0 0,0 0 0x00000001
formats: C8 YUYV UYVY XR24 AR24 RG16 XR15 AR15 XB30 AB30 XB24 AB24 XR30 AR30 XB4H AB4H # !!! 当前图层支持得formats
props:
8 type:
flags: immutable enum
enums: Overlay=0 Primary=1 Cursor=2 # !!! Overlay是只中间层, Primary指wb(背景), Cursor指光标。 一般Primary和Cursor支持1个,Overlay支持1-多个;
value: 1 # value=1 所以为Primary,也就是背景
42 zpos: # zpos优先级,值在0 - 254之间
flags: range
values: 0 254
value: 0
43 alpha:
flags: range
values: 0 65535
value: 65535
44 pixel blend mode:
flags: enum
enums: None=2 Pre-multiplied=0 Coverage=1
value: 0
......
综上所述,这里写出当前支持得配置 所以我们用命令:
-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]
-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]
modetest -M i915 -D 0 -s 86@48:1920x1080 -p 31@48 # 此时应该就可以出图, 其中 48 可选择任何一个CRTC层, Connector必须选择与Encoder链接层
FAQ
module和device找不到
root@baiy-desktop:/home/baiy/workspace/proj-gpu/drm# ./builddir/tests/modetest/modetest -c
trying to open device 'i915'...failed
......
no device found
root@baiy-desktop:/home/baiy/workspace/proj-gpu/drm# ls -al /dev/dri/
total 0
drwxr-xr-x 2 root root 40 May 1 07:29 .
drwxr-xr-x 20 root root 4420 May 1 07:28 ..
首先要用root用户登录,据说要用纯字符界面,不过测试后发现也没有。 这款设备无drm
注:
1. 关闭用户图形界面
sudo systemctl set-default multi-user.target
sudo reboot
2.开启用户图形界面
sudo systemctl set-default graphical.target
sudo reboot