Linux显卡驱动,DRM显示框架简单介绍
sinye56 2024-11-14 17:42 24 浏览 0 评论
随着技术的发展,从古老的光栅命令行式的人机接口到现在的大型虚拟现实场景,计算机的显示技术始终在不停的更新换代。Linux是如何显示图形界面的呢?在这里简单的介绍一下。
一、DRM框架结构
我们看到的Linux系统界面多姿多彩,主要由两部分来实现。第一部分,窗口管理系统,比如X Windows,QWS等,负责把窗口部件渲染成一帧图形保存在显存里。第二部分,DRM(Direct Rendering Manager)框架,负责驱动显卡,把显存的内容以适当的格式传递给显示器加以显示。现在的显卡早已不仅包含了图形存储和传递的功能,还包含利用GPU渲染2D/3D图形的功能,因此,DRM框架还要负责把与GPU相关的功能通过某种方式暴露给用户空间,协助窗口系统产生美轮美奂的显示效果。 这里涉及到两个概念,窗口系统渲染和GPU渲染。
窗口系统渲染
即我们常说的GUI渲染。平时我们操作的按钮,输入框,列表框等由窗口管理系统渲染成一帧一帧的图形保存在内存中,然后通过内核把图片发送到显示器上,每一次窗口的改变都产生一帧图像,像电影一样,一帧一帧的刷新到显示器上,从而我们就看到了动态的内容。
GPU渲染
我们都知道,像大型3D游戏等应用需要大量的渲染工作,如果把这些工作放在CPU上执行将是繁重的任务,而且现在CPU是为了更好的流程控制和逻辑计算而设计的,在并行计算方便并不具有优势,所以,就有了GPU的出现。把渲染工作交给GPU,就相当于找到了一个得力的助手,而且GPU是专门为并行计算而设计的,大大减轻了CPU的负担。在实现上,相当于窗口管理系统只负责创建和管理窗口,并在窗口上挖一个矩形,但里面的内容却是交由GPU来渲染了。
二、DRM主要对象
DRM 是Linux 下的图形渲染框架,具体的说是显卡驱动框架。也就是说,一个显卡如果想在Linux中玩的溜溜溜,只有遵循DRM框架的逻辑才能把驱动程序和内核融合起来,上层的应用才可以充分的利用显卡特性,把显示功能发挥到极致。由于DRM的代码十分庞大,显卡的逻辑又特别复杂,所以很难一下子全部了解通透,只有通过对关键对象的了解,把简单的例子串起来,才能慢慢的揭开它的面纱。
DRM框架
DRM框架代码就是实现DRM具体功能的代码,实现了对显卡的文件抽象,创建显卡驱动文件(如:/dev/dri/card0)。用户程序通过open/close/ioctl 等标准接口,来驱动设备。在系统初始化的时候,内核按其他设备一样的方式加载驱动,发现设备,及其他初始化工作。 框架代码位于: drivers/gpu/drm GPU调度的代码放在 drivers/gpu/drm/scheduler TTM相关代码放在 drivers/gpu/drm/ttm 其他的子目录是各种厂家的驱动程序目录,如amd,vmwgfx等。
Framebuffer
帧缓冲区对象(struct drm_framebuffer)是帧内存对象的抽象,它提供了像素源给到CRTC。帧缓冲区依赖于底层内存管理器分配内存。应用程序通过调用ioctl,指定DRM_IOCTL_MODE_ADDFB参数显式地创建帧缓冲区,创建的对象以句柄的形式返回用户空间,随后可以给到后续CRTC,Plane及页面更新等函数。对于使用GEM缓冲区管理的驱动程序来说,这将是一个GEM句柄;使用TTM的是TTM句柄,例如vmwgfx驱动就直接向用户空间暴露TTM句柄。经过drm_framebuffer_init()函数初始化后,userspace可以使用和访问fb对象。通常通过mmap系统调用映射一段内存区,然后像读写内存一样方便的更新帧数据,之后再提交到CRTC上。帧缓冲区的生命周期由引用计数控制,驱动程序可以使用drm_framebuffer_get()获取引用,然后使用drm_framebuffer_put()释放引用。
Plane
一个平面对象(struct drm_plane)表示一个图像源,从drm_framebuffer对象获取输入数据,在输出过程中与CRTC的顶部混合或覆盖。平面本身不单指定该图像的裁剪和缩放,而且指定了它放置在CRTC中的位置,当然它还有其他属性,例如像素的位置和混合方式,旋转或Z位置等,所有属性都存储在drm_plane_state中。要创建平面,驱动程序分配初始化drm_plane对象实例,然后通过drm_universal_plane_init()注册它。光标和覆盖平面是Plane的一个实例,所有驱动程序都应该至少为每个CRTC提供一个主平面。
CRTC
一个CRTC(struct drm_crtc)表示一整个显示管线。它从drm_plane平面接收像素数据并将它们混合在一起。drm_display_mode也绑定到CRTC,指定显示时序等功能。在输出端,数据被送到一个或多个drm_encoder对象中,每个drm_encoder对象又连接到一个drm_connector对象上。 要创建CRTC,KMS驱动程序分配一个drm_crtc的一个实例,然后调用drm_crtc_init_with_planes()将其注册。CRTC仍然包含有旧模式集操作的入口点,例如drm_crtc_funcs.page_flip和drm_crtc_funcs.cursor_set2,以及drm_crtc_funcs.gamma_set等。 对于更新的atomic驱动,所有功能通过drm_property和drm_mode_config_funcs.atomic_check(),及drm_mode_config_funcs.atomic_check()进行控制。
Encoder
Encoder对象(struct drm_encoder)是CRTC和连接器(drm_connector)之间的连接元素。编码器从CRTC获取像素数据,并将其转换成适合任何连接的连接器的格式。编码器用drm_encoder_init初始化,并用drm_encoder_cleanup清理。
Connector
在DRM中,连接器(struct drm_connector)是显示接收器的抽象,包括als固定面板或任何其他可以以某种形式显示像素的东西。与表示硬件的所有其他KMS对象(如CRTC、编码器或平面抽象)不同,连接器可以在运行时热插拔,因此,使用drm_connector_get和drm_connector_put()控制引用计数。KMS驱动程序必须为每个接收器创建、初始化、注册并连接到结构drm_connector。通过drm_connector_init初始化,该调用带有指向drm_connector_funcs的指针和连接器类型,然后通过对drm_connector_register的调用公开给用户空间。连接器必须连接到编码器才能使用。
三、Drm简单例子
从例子开始学习是一个比较快捷的方法,先试一个最简单的modeset,显示一副纯色的图形。注意:例子不能在Linux桌面环境下执行,因为设备已经被窗口系统占用了,需要通过命令
[root@localhost ~]#init 3
把系统切换到多用户模式。 通过代码可以看到例子的主要步骤如下:
1. 打开设备文件
Drm框架在加载成功后会创建一个/dev/dri/card0设备文件,给用户程序一个统一的入口,操作显卡的各种特性,使用的时候通过文件系统调用打开即可。
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "cannot open '%s': %m\n", node);
return ret;
}
2. 获取资源句柄
获取所有与显卡有关的资源,资源都是通过句柄来操作
/**
1. Retrives all of the resources associated with a card.
*/
extern drmModeResPtr drmModeGetResources(int fd);
3. 获取连接对象
获取到资源对象drmModeRes之后,就可以通过它获取连接对象
/**
2. Retrieve all information about the connector connectorId. This will do a
3. forced probe on the connector to retrieve remote information such as EDIDs
4. from the display device.
*/
extern drmModeConnectorPtr drmModeGetConnector(int fd,
uint32_t connectorId);
4. 创建FB
创建FrameBuffer,然后映射出一块内存,之后在内存中填入像素数据
static int modeset_create_fb(int fd, struct modeset_dev *dev)
{
struct drm_mode_create_dumb creq;
struct drm_mode_destroy_dumb dreq;
struct drm_mode_map_dumb mreq;
int ret;
/* create dumb buffer */
memset(&creq, 0, sizeof(creq));
creq.width = dev->width;
creq.height = dev->height;
creq.bpp = 32;
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
if (ret < 0) {
fprintf(stderr, "cannot create dumb buffer (%d): %m\n",
errno);
return -errno;
}
dev->stride = creq.pitch;
dev->size = creq.size;
dev->handle = creq.handle;
/* create framebuffer object for the dumb-buffer */
ret = drmModeAddFB(fd, dev->width, dev->height, 24, 32, dev->stride,
dev->handle, &dev->fb);
if (ret) {
fprintf(stderr, "cannot create framebuffer (%d): %m\n",
errno);
ret = -errno;
goto err_destroy;
}
/* prepare buffer for memory mapping */
memset(&mreq, 0, sizeof(mreq));
mreq.handle = dev->handle;
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret) {
fprintf(stderr, "cannot map dumb buffer (%d): %m\n",
errno);
ret = -errno;
goto err_fb;
}
/* perform actual memory mapping */
dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, mreq.offset);
if (dev->map == MAP_FAILED) {
fprintf(stderr, "cannot mmap dumb buffer (%d): %m\n",
errno);
ret = -errno;
goto err_fb;
}
/* clear the framebuffer to 0 */
memset(dev->map, 0, dev->size);
return 0;
err_fb:
drmModeRmFB(fd, dev->fb);
err_destroy:
memset(&dreq, 0, sizeof(dreq));
dreq.handle = dev->handle;
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
return ret;
}
5. 设置Crtc模式
FB创建好了,并且清0了,也可以填充任何数据,然后设CRTC,FB的内容就会显示到屏幕上,drmModeSetCrtc的参数有fd,crtc句柄,FB句柄,x,y坐标等
/**
* Set the mode on a crtc crtcId with the given mode modeId.
*/
int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
uint32_t x, uint32_t y, uint32_t *connectors, int count,
drmModeModeInfoPtr mode);
6. 清理工作
显示完成后,可以执行清理工作。一般的GUI会一直运行下去,不会自己执行清楚工作,但为了程序的完整性,还是要包含清理资源的代码。
static void modeset_cleanup(int fd)
{
struct modeset_dev *iter;
struct drm_mode_destroy_dumb dreq;
while (modeset_list) {
/* remove from global list */
iter = modeset_list;
modeset_list = iter->next;
/* restore saved CRTC configuration */
drmModeSetCrtc(fd,
iter->saved_crtc->crtc_id,
iter->saved_crtc->buffer_id,
iter->saved_crtc->x,
iter->saved_crtc->y,
&iter->conn,
1,
&iter->saved_crtc->mode);
drmModeFreeCrtc(iter->saved_crtc);
/* unmap buffer */
munmap(iter->map, iter->size);
/* delete framebuffer */
drmModeRmFB(fd, iter->fb);
/* delete dumb buffer */
memset(&dreq, 0, sizeof(dreq));
dreq.handle = iter->handle;
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
/* free allocated memory */
free(iter);
}
}
例子的源码是参考网上的,地址: https://github.com/dvdhrm/docs/tree/master/drm-howto 里面有几个例子,都可以在虚拟机上运行通过
参考文章
DRM(Direct Rendering Manager)学习简介
https://blog.csdn.net/hexiaolong2009/article/details/83720940
Linux DRM(二)基本概念和特性
https://blog.csdn.net/dearsq/article/details/78394388
例子代码
https://github.com/dvdhrm/docs/tree/master/drm-howto
相关推荐
- 程序员:JDK的安装与配置(完整版)_jdk的安装方法
-
对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...
- Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置
-
一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...
- 麒麟系统安装JAVA JDK教程_麒麟系统配置jdk
-
检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...
- 学习笔记-Linux JDK - 安装&配置
-
前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...
- Linux新手入门系列:Linux下jdk安装配置
-
本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...
- 测试员必备:Linux下安装JDK 1.8你必须知道的那些事
-
1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...
- Linux 下安装JDK17_linux 安装jdk1.8 yum
-
一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...
- 在Ubuntu系统中安装JDK 17并配置环境变量教程
-
在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...
- 如何在 Linux 上安装 Java_linux安装java的步骤
-
在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...
- Windows和Linux环境下的JDK安装教程
-
JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...
- linux安装jdk_linux安装jdk软连接
-
JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...
- linux安装jdk,全局配置,不同用户不同jdk
-
jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...
- 运维大神教你在linux下安装jdk8_linux安装jdk1.7
-
1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...
- window和linux安装JDK1.8_linux 安装jdk1.8.tar
-
Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...
- 最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?
-
默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- linux进程通信 (63)