DRM框架简介
传统的Linux 显示设备驱动开发时,通常使用FB驱动架构;随着显卡性能的升级,显示覆盖(菜单层级),GPU加速,硬件光标,传统的FB架构都无法很好的支持,此外,对于多应用的访问冲突也无法很好的控制,这样的背景下,DRM应运而生
DRM 是目前主流的图形显示框架,Linux 内核中已经有Framebuffer 驱动用于管理显示设备的 Framebuffer,Framebuffer 框架也可以实现Linux 系统的显示功能,但是缺点如下:
- DRM 原生支持多图层合成,FB架构原生不支持多图层合成
- FB不支持VSYNC,DMA-BUF,异步更新和fence机制,DRM都支持
这些功能DRM框架都支持, 可以统一管理GPU和Display驱动,使得软件架构更为统一,方便管理和维护
下面是Linux graphic system 的框架,基于Wayland的Windowing system,在DRI框架下,通过两条路径(DRM和KMS),分别实现Rendering和送显两个显示步骤,注意观察 DRM 框架在其中的作用
Application(如3D game)根据用户动作,需要重绘界面,此时它会通过OpenGL_ES、EGL等接口,将一系列的绘图请求,提交给GPU
注意:
a)OpenGL_ES、EGL的实现,可以有多种形式,这里以Mesa 3D为例,所有的3D rendering请求,都会经过该软件库,它会根据实际情况,通过硬件或者软件的方式,响应Application的rendering请求
b)当系统存在基于DRI的硬件rendering机制时,Mesa 3D会通过libGL-meas-DRI,调用DRI提供的rendering功能
c)libGL-meas-DRI会调用libdrm,libdrm会通过ioctl调用kernel态的DRI驱动,这里称作DRM(Direct Rendering Module)
d)kernel的DRM模块,最终通过GPU完成rendering动作GPU绘制完成后,将rendering的结果返回给Application。
rendering的结果是以image buffer的形式返回给应用程序。Application将这些绘制完成的图像buffer(可能不止一个)送给Wayland compositor,Wayland compositor会控制硬件,将buffer显示到屏幕上。
Wayland compositor会搜集系统Applications送来的所有image buffers,并处理buffer在屏幕上的坐标、叠加方式后,直接通过ioctl,交给kernel KMS(kernel mode setting)模块,该模块会控制显示控制器将图像显示到具体的显示设备上。
DRM 基本概念
DRM 框架的基本流程框图如下:
软件角度框图:
DRM框架涉及到的元素很多,大致如下:
KMS:CRTC,ENCODER,CONNECTOR,PLANE,FB,VBLANK,property
GEM:DUMB、PRIME、Fence
下面一一介绍
libdrm:
应用层的一个动态链接库,对底层接口进行封装,向上层提供通用的API接口,主要是对各种IOCTL接口进行封装
KMS:
Kernel mode setting 简而言之做两件事:更新画面 + 设置显示参数
更新画面: 显示buffer 的切换,多图层之间的合成方式,每个图层的显示位置
设置显示参数:包含 刷新率,分辨率,电源状态 休眠唤醒等等
GEM:
Graphic Execution Manager,主要负责显示buffer的分配和释放,也是GPU唯一用到DRM的地方
元素 | 说明 |
---|---|
CRTC | 对显示buffer 进行扫描产生时序的硬件模块,通常是指 Display Controller |
Encoder | 负责将CRTC输出的Timing 转换为外部设备需要信号的硬件模块:比如 HDMI 转换器,LVDS转换器,MIPI/DSI 转换器等 |
Connector | 连接物理显示设备的连接器,比如 HDMI接口,LVDS 接口等,通常和 Encoder 绑定在一起 |
Plane | 硬件图层,一个 plane 对应于一张显示buffer,有的 显示设备支持多图层显示(overlay mode),但所有的Display Controller至少要有1个plane |
FB | Framebuffer ,单个图层的显示buffer,对应于一段显存 |
VBlank | 软件和硬件的同步机制,LCD时序中两帧之间的垂直消隐区,软件通常使用硬件VSYNC来实现 |
Property | DRM 中用于设置显示参数的数据结构,所有设置到显示设备的参数都可以抽象为 property |
Dumb | 表示连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景 |
Prime | 连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存复杂场景 |
Fence | Buffer 之间的同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题 |
学习 drm 就是学习上面这些概念的用法和实现。
为什么 Linux 内核 drm 框架要使用 libdrm 这个空间库?
提供一个抽象层,保持兼容性,便于跨平台
libdrm 提供了一个抽象层,隐藏了与 DRM 设备交互的底层细节。它为应用程序开发者提供了一套简化的接口,使得开发与 DRM 相关的应用程序变得更加容易。
由于 libdrm 封装了与 DRM 内核模块交互的细节,它可以帮助应用程序保持与不同版本的 DRM 内核模块的兼容性。即使内核模块的内部实现发生了变化,只要 libdrm 的 API 保持不变,应用程序就不需要做出相应的修改。提供访问控制,保证安全性
libdrm 通过提供安全的 API 来防止应用程序执行可能导致系统不稳定或安全漏洞的操作。它可以帮助确保应用程序只能执行它们有权限执行的操作。
在 Linux 系统中,DRM 设备节点通常位于 /dev/dri/ 目录下,例如 /dev/dri/card0 和 /dev/dri/renderD128。这些设备节点的访问权限通常受到限制,只有特定的用户组(如 video)的成员才能访问。这意味着只有属于 video 组的应用程序才能打开这些设备节点并与之交互。
此外,libdrm 还通过其 API 提供了一种机制,用于验证传递给 DRM 驱动的参数。例如,当应用程序尝试设置显示控制器(CRTC)的状态时,它会使用 libdrm 提供的函数,如 drmModeSetCrtc()。libdrm 在将这些调用转发给内核空间的 DRM 驱动之前,会检查参数的有效性,从而防止无效的参数导致系统不稳定或安全问题
3.提供一些实用的工具或者程序,用于调试或者验证Drm 驱动
比如 modetest 程序,常被用来验证 drm 驱动程序是否正确的加载了
4.开源社区的支持
libdrm 是一个开源项目,由一个活跃的社区维护。这意味着它能够及时更新,以支持新的 DRM 功能和改进。
VBlank 和 VSYNC 有什么区别
VBlank(垂直空白期或者垂直消隐区): VBlank 是显示器屏幕上两个垂直扫描线之间的时间间隔,在这个时间段内,电子束从屏幕底部扫回到顶部,准备开始下一帧的扫描。这个时间段内,显示器不会显示任何图像内容,因此称为“空白期”。VBlank 期间,显卡可以安全地更新屏幕上的内容,而不会干扰正在显示的图像。在 VBlank 期间,显卡可以执行诸如交换前后缓冲区(double buffering)、处理用户输入、更新游戏逻辑等操作。(在这个区间内可以交换前后缓冲器,更新寄存器等操作)
VSYNC(垂直同步): VSYNC 是一个信号或机制,用于同步显卡的渲染周期和显示器的刷新周期。当 VSYNC 功能启用时,显卡会等待显示器的 VBlank 期间到来时才进行前后缓冲区的交换,这样可以避免在屏幕显示图像时发生图像撕裂(tearing)的现象。图像撕裂是指屏幕上显示的图像由来自两个不同渲染周期的部分组成,导致视觉上的不连续。
总结一下区别:
- VBlank 是显示器刷新周期中的一个特定时间段,此时屏幕上不显示任何图像内容。
- VSYNC 是一个同步机制,用于控制显卡在何时交换缓冲区并更新屏幕上的图像,以避免图像撕裂。
在实际应用中,是否启用 VSYNC 会影响游戏的性能和视觉体验。启用 VSYNC 可以提供更平滑的视觉效果,但可能会增加输入延迟,并且在显卡性能不足以跟上显示器刷新率时,可能会导致帧率下降。关闭 VSYNC 可以减少输入延迟,提高性能,但可能会出现图像撕裂现象。有些应用程序和游戏提供了“适应性 VSYNC”或“可变刷新率”功能,旨在结合两者的优点,根据当前的渲染性能动态调整 VSYNC 的状态。