Linux graphic subsystem(2)_DRI介绍

作者:wowo 发布于:2015-12-27 22:02 分类:图形子系统

1. 前言

上篇文章(Linux graphic subsytem(1)_概述)介绍了linux图形子系统基本的软件框架,以及GUI、Windowing system、3D渲染等基本概念。文中提到了linux DRI(Direct Render Infrastructure)框架,但限于篇幅,没有过多介绍。

蜗蜗觉得,DRI在当前(或者说将来)的linux图形子系统中,有着举足轻重的地位,甚至可以说是新的linux图形框架核心思想的体现。本文将基于linux图形框架的发展历程,从Why、What和How三个角度,介绍DRI框架。

2. 为什么需要DRI

在GUI环境中,一个Application想要将自身的UI界面呈现给用户,需要2个步骤:

1)根据实际情况,将UI绘制出来,以一定的格式,保存在buffer中。该过程就是常说的“Rendering”。

不知道为什么,wowo一直觉得“Render”这个英文单词太专业、太抽象了,理解起来有些困难。时间久了,也就不再执著了,看到它时,就想象一下内存中的图像数据(RGB或YUV格式),Rendering就是生成它们的过程。

通常来说,Rendering有多种表现形式,但可归结为如下几类:

a)2D的点、线、面等绘图,例如,“通过一个for循环,生成一个大小为640x480、格式为RGB888、填充颜色为红色的矩形框”,就是一个2D rendering的例子。

b)3D渲染。该过程牵涉比较复杂的专业知识,这里先不举例了。

c)图片、视频等多媒体解码。

d)字体渲染,例如直接从字库中抽出。

2)将保存在buffer中的UI数据,显示在display device上。该过程一般称作“送显”。

然后问题就来了:这两个步骤中,display server要承担什么样的角色?回答这个问题之前,我们需要知道这样的一个理念:

在操作系统中,Application不应该直接访问硬件,通常的软件框架是(从上到下):Application<---->Service<---->Driver<---->Hardware。这样考虑的原因主要有二:安全性和共享硬件资源(例如显示设备只有一个,却有多个应用想要显示)。

对稍微有经验的软件开发人员(特别是系统工程师和驱动工程师)来说,这种理念就像杀人偿命、欠债还钱一样天经地义。但直到X server+3D出现之后,一切都不好了。因为X server大喊的着:“让我来!”,给出了这样的框架:

x_window_indirect_render

先不考虑上面的GLX、Utah GLX等术语,我们只需要理解一点即可:

基于OpenGL的3D program需要进行3D rendering的时候,需要通过X server的一个扩展(GLX),请求X server帮忙处理。X server再通过底层的driver(位于用户空间),通过kernel,访问硬件(如GPU)。

其它普通的2D rendering,如2D绘图、字体等,则直接请求X server帮忙完成。

看着不错哦,完全满足上面的理念。但计算机游戏、图形设备硬件等开发人员不乐意了:请让我们直接访问硬件!因为很多高性能的图形设备,要求相应的应用程序直接访问硬件,才能实现性能最优[1]

好像每个人都是对的,怎么办?妥协的结果是,为3D Rendering另起炉灶,给出一个直接访问硬件的框架,DRI就应运而生了,如下:

x_window_direct_render

上面好像讲的都是Rendering有关的内容,那送显呢?还是由display server统一处理比较好,因为显示设备是有限的,多个应用程序的多个界面都要争取这有限的资源,server会统一管理、叠加并显示到屏幕上。而这里叠加的过程,通常称作合成(Compositor),后续文章会重点说明。

3. 软件架构

DRI是因3D而生,但它却不仅仅是为3D而存在,这背后涉及了最近Linux图形系统设计思路的转变,即:

从以前的:X serve是宇宙的中心,其它的接口都要和我对话。

转变为:Linux kernel及其组件为中心,X server(如Wayland compositor等)只是角落里的一员,可有可无。

最终,基于DRI的linux图形系统如下(参考自[4][5]):

Direct_Rendering_Infrastructure

该框架以基于Wayland的Windowing system为例,描述了linux graphic系统在DRI框架下,通过两条路径(DRM和KMS),分别实现Rendering和送显两个显示步骤。从应用的角度,显示流程是:

1)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动作。

2)GPU绘制完成后,将rendering的结果返回给Application。

rendering的结果是以image buffer的形式返回给应用程序。

3)Application将这些绘制完成的图像buffer(可能不知一个)送给Wayland compositor,Wayland compositor会控制硬件,将buffer显示到屏幕上。

Wayland compositor会搜集系统Applications送来的所有image buffers,并处理buffer在屏幕上的坐标、叠加方式后,直接通过ioctl,交给kernel KMS(kernel mode setting)模块,该模块会控制显示控制器将图像显示到具体的显示设备上。

4. DRM和KMS

DRM是Direct Rendering Module的缩写,是DRI框架在kernel中的实现,负责管理GPU(或显卡,graphics card)及相应的graphics memory,主要功能有二:

1)统一管理、调度多个应用程序向显卡发送的命令请求,可以类比为管理CPU资源的进程管理(process management)模块。

2)统一管理显示有关的memory(memory可以是GPU专用的,也可以是system ram划给GPU的,后一种方法在嵌入式系统比较常用),该功能由GEM(Graphics Execution Manager)模块实现,主要包括:

a) 允许用户空间程序创建、管理、销毁video memory对象(称作“"GEM objects”,以handle为句柄)。

b)允许不同用户空间程序共享同一个"GEM objects”(需要将不唯一的handle转换为同一个driver唯一的GEM name,后续使用dma buf)。

c)处理CPU和GPU之间内存一致性的问题。

d)video memory都在kernel管理,便于给到display controller进行送显(Application只需要把句柄通过Wayland Compositor递给kernel即可,kernel会自行获取memory及其内容)。

KMS是Kernel Mode Setting的缩写,也称作Atomic KMS,它是一个在linux 4.2版本的kernel上,才最终定性的技术。从字面意义上理解,它要实现的功能比较简单,即:显示模式(display mode)的设置,包括屏幕分辨率(resolution)、颜色深的(color depth)、屏幕刷新率(refresh rate)等等。一般来说,是通过控制display controller的来实现上述功能的。

也许大家会有疑问:这些功能和DRI有什么关系?说实话,关系不大,之所以要在DRI框架里面提及KMS,完全是历史原因,导致KMS的代码,放到DRM中实现了。目前的kernel版本(如4.2之后),KMS和DRM基本上没有什么逻辑耦合(除了代码位于相同目录,以及通过相同的设备节点提供ioctl之外),可以当做独立模块看待。

继续上面的话题,只是简单的display mode设置的话,代码实现不复杂吧?还真不一定!相反,KMS有关的技术背景、软件实现等,是相当复杂的,因此也就不能三言两语说得清,我会在单独的文章中重点分析KMS。

5. 参考文档

[1]: https://en.wikipedia.org/wiki/Direct_Rendering_Infrastructure

[2]: https://en.wikipedia.org/wiki/Wayland_(display_server_protocol)

[3]: http://wayland.freedesktop.org/architecture.html

[4]: Linux_kernel_and_daemons_with_exclusive_access.svg

[5]: Wayland_display_server_protocol.svg

 

原创文章,转发请注明出处。蜗窝科技,www.wowotech.net

标签: DRI drm kms rendering

评论:

XYZ
2021-12-10 15:21
能讲讲DRM中的GEM, TTM吗
llp
2021-11-12 09:32
wowo大佬,还有后续的教程吗
wowo
2021-11-16 08:46
@llp:抱歉啊!暂时没时间写了
11号tony老师
2019-12-03 14:09
大神请教下.
问题1:
/dev/dri/card0
/dev/dri/renderXX
这俩个和/dev/fb 是什么样的千丝万缕的关系.
drm会模拟fb,请问这句话是什么意思?
能详细解释下么?

问题2:
drm其实还是GPU的驱动对吧?
上层的lib库其实最终还是通过drm的ioclt或者节点访问形式来操作GPU,大神可以举例相关操作么,比如如何空置GPU渲染等等.?

感谢大神
minicoco
2021-03-18 19:25
@11号tony老师:问题一:
/dev/dri/card0 显示管理+显示合成(fb)+3D加速
/dev/dri/renderXX 仅仅包括3D加速
drm会模拟fb,/dev/dri/card0在fb功能的使用上跟framebuffer没什么太大区别
card0与renderXX一一对应,可以认为renderXX只是card0的一个子集,renderXX多数情况下用作离线渲染,渲染结果可以交由cardX显示(一般用gbm框架传递数据)。cardX也可以做离线渲染,单独给出一个renderXX的意义目前我也搞不清楚。

问题二:
drm包括了很多功能,GPU驱动只是其中一部分,不知道叫GPU驱动框架会不会更合适。具体渲染操作可以参考libdrm的demo,也可以看我的博客(如果我更新了的话)
melo
2022-11-29 19:46
@11号tony老师:1 card0这个节点, 可以通过open打开, 获取crtc encoder connector fb等资源, 实现设置分辨率, 刷新率等设置
renderxx这个节点, 应该是card0的子集, 只有3d加速部分, 实际没看到过怎么打开使用
fb这个是一个被drm淘汰下来的陈旧显示模块

2 drm包括kms和gem, kms是和显示相关的, 抽象出来几个概念例如crtc connector encoder, gem是管理video memory相关的, 也是drm中和gpu有联系的唯一一个部分(我是这么觉得的)
gmnail
2019-05-08 11:39
想请问一下,在某个文档里看到了这一句话:DRM本身实现了GPUs的切换,要使用 GPU 的程序将请求发送给 DRM,由 DRM 作为仲裁来避免冲突。但是我看了内核源码也没找到这里所说的实现。
另外,我有两块相同的GPU,如何实现DRM同时对他们的控制呢?是需要同时注册两个驱动程序么?
minicoco
2021-03-18 19:30
@gmnail:两块显卡会在/dev/dri/目录先生成4个设备文件,如果不带3D功能的显卡会缺少renderXX设备文件,同时控制使用离线渲染即可,libdrm与mesa opengl都可以实现。最终渲染显卡只有一个,切换渲染目标显卡可以在程序中控制,libdrm中打开正确的设备文件和配置好链路就可以,实现多张显卡同时渲染和显示都是可以的。目前xorg貌似不支持多显卡同时显示,oxrg/wayland切换显卡的方法是在debugfs中关掉不需要使用的显卡的电源。
超哥
2018-12-18 19:49
感谢楼主分享,受教了!
ctwillson
2018-10-17 14:39
后续的是不是没了?
J.Kerry
2016-09-06 16:30
HDMI的驱动,应该算是KMS对吧,如果只有这个KMS驱动,而没有DRM的话,二维的窗口系统应该可以表示的吧?
wowo
2016-09-06 17:24
@J.Kerry:理论上是可以的,不过显示有关的buffer,还是依赖drm管理的,因此要做的话,就kms+drm一起做好了(和3D、GPU没有必然联系)~~
J.Kerry
2016-09-07 10:41
@wowo:谢谢你的回答,原来DRM还是要做的。我正准备做一个HDMI的驱动,也不知道用framebuffer/v4l2/KMS,DRM的那个好,看样子还是framebuffer最简单了,就用framebuffer吧。。。
jinxinxin163
2016-04-06 14:40
特意注册了一个,讲的非常不错!
momo
2016-01-23 13:53
wowo 提个问题,在DRM/KMS显示框架下应该如何录屏呢?
FrameBuffer显示框架下录屏比较容易实现。
谢谢~
wowo
2016-01-25 08:39
@momo:录屏不应该和底层的实现有关,因此一般都在合成器(Composer)里面做。例如Android,录屏也是一个Display device,只是目的地是内存。
momo
2016-01-25 10:44
@wowo:谢谢wowo。录屏的确是应用层的事情,但底层应该开放相应的entity给应用录屏,比如说
FrameBuffer显示框架可以通过访问/dev/fb0来录屏,Android应该也可以访问/dev/ graphics/fb0来录屏(没做过Android,此处仅是猜测)。DRI开放给应用程序的entity有/dev/dri/card0和/dev/dri/controlD64,而据我所知都不能用来录屏(也许可以只是我不知道)。所以想请教一下wowo,在DRI显示框架下,有什么好的办法或者建议来录屏?
wowo
2016-01-25 15:16
DRI所在的应用场景,远比Framebufffer复杂,因为硬件可以做很多事情,我们举一个支持双显的例子(这在现今的消费类系统是很常见的):

Plane 0--------------\
                                |
Plane 1----------\    |
                            |   |
                           V V
Plane 2---->CRTC0------>Encoder0---->LCD
      (Scanout buffer 0)

Plane 3----------\
                           |
                          V
Plane 4---->CRTC1------>Encoder1---->HDMI
      (Scanout buffer 1)

问题的关键点是:
1)Application可以同时送给DRI 7个framebuffer(Plane和Scanout buffer都是由framebuffer表示)。
2)而我们的硬件(CRTC),可以对这多个buffer自行处理(缩放、叠加、Alpha混合等)后,驱动显示控制器(Encoder)输出。

在这个过程中,我们不能简单的抓取任何一个framebuffer就可以完成屏幕录像。如果要做,只能在如下的2几个地方做:
1)我们的CRTC在将合成后的图像送到Encoder的时候,同时送一份给内存,其实就是硬件的回写功能。
2)由Application自行完成(这里的Application可能是合成器,如Wayland)。Application在将buffer送给CRTC的同时,调用软件API(OpenGL等))将这些buffer合成为一个,然后保存成文件。至于合成器是够提供这个功能,我也不是很清楚,要看你是使用什么样的GUI系统。

当然,如果系统没有Plane,每个CRTC也就只有一个Scanout buffer,就可以将DRI降级为简单的framebuffer设备。旧的方法(/dev/fbX)依旧是可行的。
momo
2016-01-26 10:56
@wowo:谢谢wowo,讲得很清楚~
准备使用硬件的回写功能来实现,对我这样的新手是个挑战。
wangsuyu_1
2015-12-31 16:07
很期待KMS的分析,最近研究drm模块头都快大了,源码注释也太少了。顺便想问一下窝窝,看源码的时候如果没有注释,比如某个结构体里面的成员变量不知道做什么用的,某个函数在这里调用作用是什么的时候,有什么好的渠道去了解吗?谢谢。
wowo
2016-01-02 10:41
@wangsuyu_1:说实话,DRM/KMS我也是边学边写。
至于你的问题,我觉得这个时候从上到下看会比较容易一些,先弄清楚使用这个模块的上层模块,到底要做什么事情,怎么通过这个模块做这些事情的,带着问题去看,效果应该好一些。
wangsuyu_1
2016-01-03 12:56
@wowo:谢谢wowo,持续关注中。

发表评论:

Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog