本文主要介绍libdrm
的代码下载、编译和调试的工作。新版本的libdrm不再采用configure && make的方式编译,而是改用meson && ninja编译方式,近些年很多多媒体的开源软件包的构建系统有向后者靠拢的趋势,典型的比如gstream及其各类PLUGIN。
开发环境
LIBDRM下载:
由于使用的是UBUNTU18.4系统,选择其发布周期内的LIBDRM版本2.4.96版。
下载地址https://dri.freedesktop.org/libdrm/libdrm-2.4.96.tar.gz
编译
由于DRM的后端为GPU,而GPU和HOST之间通常以PCI/PCIe接口互联,所以LIBDRM对PCI库有依赖,需要提前安装:
sudo apt install libpciaccess-dev
之后执行如下命令进行配置
./configure --enable-install-test-programs --enable-omap-experimental-api --prefix=/home/zlcao/Workspace/libdrm/install
--enable-install-test-programs目的是编译测试工具,比如modetest工具是调试KMS参数的利器。--enable-omap-experimental-api纯粹为了验证选项,没有其它目的。
如果一切OK,配置结束后会输出编译配置summary:
之后,执行make && make install即可,可以看到,SDK包括测试工具,测试库和库对应的头文件,其结构是一个典型的SDK发布目录的结构。
zlcao@zlcao-Vostro-3268:~/Workspace/libdrm/install$ tree -L 2
.
├── bin
│ ├── drmdevice
│ ├── kms-steal-crtc
│ ├── kmstest
│ ├── kms-universal-planes
│ ├── modeprint
│ ├── modetest
│ ├── proptest
│ └── vbltest
├── include
│ ├── libdrm
│ ├── libkms
│ ├── libsync.h
│ ├── omap
│ ├── xf86drm.h
│ └── xf86drmMode.h
├── lib
│ ├── libdrm_amdgpu.la
│ ├── libdrm_amdgpu.so -> libdrm_amdgpu.so.1.0.0
│ ├── libdrm_amdgpu.so.1 -> libdrm_amdgpu.so.1.0.0
│ ├── libdrm_amdgpu.so.1.0.0
│ ├── libdrm_intel.la
│ ├── libdrm_intel.so -> libdrm_intel.so.1.0.0
│ ├── libdrm_intel.so.1 -> libdrm_intel.so.1.0.0
│ ├── libdrm_intel.so.1.0.0
│ ├── libdrm.la
│ ├── libdrm_nouveau.la
│ ├── libdrm_nouveau.so -> libdrm_nouveau.so.2.0.0
│ ├── libdrm_nouveau.so.2 -> libdrm_nouveau.so.2.0.0
│ ├── libdrm_nouveau.so.2.0.0
│ ├── libdrm_omap.la
│ ├── libdrm_omap.so -> libdrm_omap.so.1.0.0
│ ├── libdrm_omap.so.1 -> libdrm_omap.so.1.0.0
│ ├── libdrm_omap.so.1.0.0
│ ├── libdrm_radeon.la
│ ├── libdrm_radeon.so -> libdrm_radeon.so.1.0.1
│ ├── libdrm_radeon.so.1 -> libdrm_radeon.so.1.0.1
│ ├── libdrm_radeon.so.1.0.1
│ ├── libdrm.so -> libdrm.so.2.4.0
│ ├── libdrm.so.2 -> libdrm.so.2.4.0
│ ├── libdrm.so.2.4.0
│ ├── libkms.la
│ ├── libkms.so -> libkms.so.1.0.0
│ ├── libkms.so.1 -> libkms.so.1.0.0
│ ├── libkms.so.1.0.0
│ └── pkgconfig
└── share
├── libdrm
└── man
10 directories, 39 files
测试命令
在图形终端下,/dev/dri/card0
被占用。有些测试程序无法执行,需要chvt切换到字符终端中执行。
drmdevice可以在图形界面下执行,它会枚举DRM设备信息,联合LSPCI命令的输出,可以确定DRM设备的类型,比如下图表示我的DRM设备PCI BDF为:00:02.0,是INTEL I915集成显卡。
zlcao@zlcao-Vostro-3268:~/Workspace/libdrm/install/bin$ ./drmdevice
--- Checking the number of DRM device available ---
--- Devices reported 1 ---
--- Retrieving devices information (PCI device revision is ignored) ---
device[0]
+-> available_nodes 0x05
+-> nodes
| +-> nodes[0] /dev/dri/card0
| +-> nodes[2] /dev/dri/renderD128
+-> bustype 0000
| +-> pci
| +-> domain 0000
| +-> bus 00
| +-> dev 02
| +-> func 0
+-> deviceinfo
+-> pci
+-> vendor_id 8086
+-> device_id 5912
+-> subvendor_id 1028
+-> subdevice_id 0762
+-> revision_id IGNORED
--- Opening device node /dev/dri/card0 ---
--- Retrieving device info, for node /dev/dri/card0 ---
device[0]
+-> available_nodes 0x05
+-> nodes
| +-> nodes[0] /dev/dri/card0
| +-> nodes[2] /dev/dri/renderD128
+-> bustype 0000
| +-> pci
| +-> domain 0000
| +-> bus 00
| +-> dev 02
| +-> func 0
+-> deviceinfo
+-> pci
+-> vendor_id 8086
+-> device_id 5912
+-> subvendor_id 1028
+-> subdevice_id 0762
+-> revision_id 04
--- Opening device node /dev/dri/renderD128 ---
--- Retrieving device info, for node /dev/dri/renderD128 ---
device[0]
+-> available_nodes 0x05
+-> nodes
| +-> nodes[0] /dev/dri/card0
| +-> nodes[2] /dev/dri/renderD128
+-> bustype 0000
| +-> pci
| +-> domain 0000
| +-> bus 00
| +-> dev 02
| +-> func 0
+-> deviceinfo
+-> pci
+-> vendor_id 8086
+-> device_id 5912
+-> subvendor_id 1028
+-> subdevice_id 0762
+-> revision_id 04
modetest:
vbltest查看刷新率:
Linux 获取VSYNC核心原理解读
我们首先要理解几个基础概念,行同步/场同步/行消隐/场消隐,如下所示:
行同步HSYNC:电视信号发送端为了使接收端的行扫描与场扫描规律与其同步,在行扫描正常结束后,向接收机发出一个脉冲信号,表示这一行已经结束,这个脉冲信号就是行同步信号。
场同步VSYNC:电视信号发送端为了使接收端的行扫描与场扫描规律与其同步,在场扫描正常结束后,向接收机发出一个脉冲信号,表示这一场已经结束,这个脉冲信号就是场同步信号。
行消隐HBLANK:指行与行之间的返回过程。而扫描点扫描完一帧后,要从图像的右下角返回到图像的左上角,开始新一帧的扫描,这一时间间隔,叫做行消隐。
场消隐VBLANK:扫描点扫描完一帧后,要从图像的右下角返回到图像的左上角,开始新一帧的扫描,这一时间间隔,叫做垂直消隐,也称场消隐。
使用Linux系统 libdrm库 获取Vsync的核心原理:使用drmWaitVBlank()可以获得场消隐VBlank,可以简单理解为要想获得2个Vsync之间的时间间隔,可以通过获得 场消隐VBlank之间的时间间隔,因为每一次场消隐伴随着场同步信号VSYNC的触发。
有了这个理解,使用drmWaitVBlank方法就可以使用VSYNC信号了,demo源码如下所示:
#include <stdio.h>
#include <xf86drm.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>
#define DRM_VBLANK_HIGH_CRTC_SHIFT 1
struct timeval tp1, tp2;
void onVsync(){
long usec1 = 0;
gettimeofday(&tp2, NULL);
usec1 = 1000000 * (tp2.tv_sec - tp1.tv_sec) + (tp2.tv_usec - tp1.tv_usec);
printf("onVsync= %f ms \n",usec1/1000.0f);
gettimeofday(&tp1, NULL);
}
int main(int argc, char** argv)
{
int ret = 0;
drmVBlank vbl;
memset(&vbl, 0, sizeof(vbl));
vbl.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE);;
vbl.request.sequence = 0;
//open card0 device point
int fd = open("/dev/dri/card0",O_RDWR);
if ( fd<0 ) {
printf("failed to open /dev/dri/card0");
return -1;
}
gettimeofday(&tp1, NULL);
while (1) {
uint32_t high_crtc = (0 << DRM_VBLANK_HIGH_CRTC_SHIFT);
vbl.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK) );
vbl.request.sequence = 1;
//wait next vsync
ret = drmWaitVBlank(fd, &vbl);
if (ret != 0) {
printf("drmWaitVBlank failed ret=%d\n", ret);
return -1;
}
//vsync callback
onVsync();
}
return 0;
}
得到VBLANK周期为13.8ms,对应到刷新频率为72,和vbitest结论相符。
查找DRM服务的哪块屏幕?
使用xrandr命令去看,这时候主要是确定对应的接口类型是 HDMI、DP还是DSI接口。同时也确定对应的数字,比如,我的PC上有DP和HDMI接口,当前连接的是DRM接口,执行xrandr后显示如下: