图形驱动

参考

图形驱动技术栈概览

图形驱动的秘密-其他文章也很不错

wiki-DRM

DRI

用户态驱动

Mesa & Gullium3D

mesa

Mesa (或Mesa3D)是一个 OpenGL/OpenGlES/Vulkan 的参考实现,以及为所有图形程序提供各种 GL 的入口点。

Mesa功能:

  • 1.对接各种GPU硬件(硬件要有DRM render driver),将应用层对GL API的调用转换到对硬件GPU的调用上(实际上是DRM驱动);
  • 2.各种 GL API 的纯软实现,当没有可用的硬件时,它可以提供传软件的 GL API 的实现(softpipe/llvmpipe/lavapipe);
  • 3.shader编译
  • 4.GPU video解码
  • 5.OpenCL计算支持

它可以用于Linux,Windows,Mac等系统平台。在Windows上运行时它提供OpenGL API over DirectX的转换。

它实现了 OpenGL,Vulkan以及其他的图形API。Mesa把这些API调用转换到相应驱动程序(其实并不是真正的驱动程序,是用户态的DRI驱动程序)调用上。AMD和Intel都提供他们对Mesa支持的驱动程序,Nvidia官方没有提供开源的支持Mesa的驱动程序,只提供了商用的驱动程序

Gallium3D

Gallium3D 是Mesa提出的用于简化GPU驱动开发的框架。
下面是Mesa结构图,展示了mesa如何通过libGL库跟内核打交道,以及展示了新旧两种用户态设备驱动程序的实现方式。

左边的驱动实现方式非常简单直接,DeviceDriver部分直接是和硬件相关的,一般由硬件厂商开发,这个方案的mesa就相当于一个OpenGL的转换器,把上层应用对OpenGL的调用转换到对特定硬件的调用上,缺点是设备厂商需要针对不同的操作系统,不同的GL API开发不同的驱动。

右边的方案是Gallium3D的方案,特点是添加了分层,它把DeviceDriver部分的上下都给抽象了出来,OS WInSys部分抽象了当前的操作系统使得同一个DeviceDriver可以用于不同的操作系统,API State Tracker部分抽象了上层的不同的GL接口,使得同一个DeviceDriver可以支持不同的GL接口。整体大大降低了DeviceDriver的开发和适配难度。

内核态驱动

DRM

DRM(Direct Rendering Manger)是linux内核子系统,负责与GPU进行接口。用户态程序可以DRM API来命令GPU进行3D渲染硬件加速、视频解码和GPGPU计算。

DRM被用来管理多个程序合作使用GPU资源,避免冲突。DRM独占访问GPU,并负责初始化和维护命令队列、内存和任何其他硬件资源。希望使用GPU的程序向DRM发送请求,DRM充当仲裁者并避免可能的冲突。

DRM的范围在过去几年里得到了扩展,涵盖了以前由用户空间程序处理的更多功能,比如framebuffer managing和mode setting、memory-sharing objects和memory sync。其中一些扩展被赋予了特定的名称,例如Graphics Execution Manager(GEM)或kernel mode setting(KMS),当专门提到它们提供的功能时,使用这些术语。但它们实际上是整个内核DRM子系统的一部分。


DRM位于内核空间,所以用户空间程序必须使用内核系统调用来请求服务。DRM遵循“一切皆文件”的Unix哲学,使用/dev下的设备文件,通过文件系统命名空间公开GPU。DRM检测到的每一个GPU都被称为一个DRM设备,并创建一个设备文件/dev/dri/cardX来提供接口。用户空间程序想与GPU通信必须首先打开该设备文件然后使用ioctl调用与DRM通信。

libdrm library是用户空间程序和DRM子系统的接口。该库仅仅是一个包装器,它为DRM API的每个ioctl提供了一个c编写的函数以及辅助的常量、结构体。libdrm不仅避免将内核接口直接暴露给应用程序,而且还能在程序之间重用和共享代码。

DRM包含两个部分:通用的“DRM core”和不同硬件特定的“DRM driver”。DRM core提供了基本的框架,不同的DRM driver可以向其注册并且还向用户空间提供了一组最少的、通用的、硬件无关的ioctl调用。DRM driver实现某种GPU支持的硬件相关的部分API。其可以拓展API,提供额外的ioctl,这些ioctl仅在此类硬件上可用的额外功能。
当特定的DRM driver提供额外的API,用户态libdrm通过额外libdrm-driver library进行扩展,用户空间程序可以使用libdrm-driver lib来和额外的ioctl来交互。

API

DRM-Master和DRM-Auth
There are several operations (ioctls) in the DRM API that either for security purposes or for concurrency issues must be restricted to be used by a single user-space process per device.[8] To implement this restriction, DRM limits such ioctls to be only invoked by the process considered the "master" of a DRM device, usually called DRM-Master. Only one of all processes that have the device node /dev/dri/cardX opened will have its file handle marked as master, specifically the first calling the SET_MASTER ioctl. Any attempt to use one of these restricted ioctls without being the DRM-Master will return an error. A process can also give up its master role—and let another process acquire it—by calling the DROP_MASTER ioctl.

The X Server—or any other display server—is commonly the process that acquires the DRM-Master status in every DRM device it manages, usually when it opens the corresponding device node during its startup, and keeps these privileges for the entire graphical session until it finishes or dies.

For the remaining user-space processes there is another way to gain the privilege to invoke some restricted operations on the DRM device called DRM-Auth. It is basically a method of authentication against the DRM device, in order to prove to it that the process has the DRM-Master's approval to get such privileges.

GEM

一种新的方法来管理内核中的graphics buffer。

GEM提供显式内存管理原语。通过GEM,用户态程序可以创建、处理、销毁GPU显存中的memory object。这些object称为GEM object,从用户态程序角度来看是持久的,并不需要每次程序获得对GPU的控制时都重新加载。当用户态程序需要一块显存(用来存储framebuffer\texture等)时,其使用GEM API向DRM driver申请分配空间。DRM driver会跟踪已使用的显存,如果空闲显存存在,则返回给用户空间一个句柄,该句柄可以在接下来的操作引用该空间。

使用GEM name来共享buffer是不安全的。通过引入DMA-BUF来解决该问题,因为DMA-BUF将用户空间的缓冲区表示为文件描述符,可以安全的共享。

另一个重要的任务是处理GPU和CPU之间内存同步。由于主存和显存都会有多级cache,因此显存管理还应处理缓存一致性,以确保CPU和GPU之间共享的数据一致。这意味着通常内存管理内部高度依赖于GPU和内存架构的硬件细节,因此是特定于驱动程序的

DMA Buffer Sharing and PRIME

DMA Buffer Sharing API(DMA-BUF)是linux内核API,其提供了一种通用的机制在多个设备之间共享DMA buffer。

KMS

mode-setting,比如设置屏幕分辨率,刷新率等操作。通常需要直接跟图形硬件打交道,能够写GPU控制器的某些寄存器。mode-setting操作必须在使用framebuffer之前进行设置。

因为一些问题将mode-setting代码移动到内核的DRM模块中,model-setting从UMS变为了KMS(kernel mode setting)。

Kernel mode setting提供了许多好处。
1.消除了重复的mode setting 代码。比如,kernel(linux console)和user space(X server driver)
2.避免在早期阶段由于模式更改而引起的闪烁
3.作为内核的一部分可以使用中断
4.支持新显示设备的热插拔

KMS device model

  • CRTCs
  • Connectors
  • Encoders
  • Planes

1.涉及元素
对 KMS 和 GEM 涉及到的组件进行概述,详细了解可参考 DRM 学习简介 | 何小龙 。
KMS:CRTC,ENCODER,CONNECTOR,PLANE,FB,VBLANK,property

GEM:DUMB,PRIME,fence

2工作流程
从用户程序的视角,要实现既定的功能,可以直接调用 DRM 框架封装的接口。
用户视角
下面以模式设置为例,简述用户程序的调用流程

打开DRM设备文件 : open("/dev/dri/card0");

获取显卡资源句柄 : drmModeGetResources(...);

获取connectorId : drmModeGetConnector(...);

创建FrameBuffer : drmModeAddFB(...);

设置Crtc模式 : drmModeSetCrtc(crtc_id, fb_id, connector_id, mode);

资源清理工作 : modeset_cleanup(...);

内核视角
然后看接口 drmModeSetCrtc() 到内核态的执行过程

Libdrm层 : drmModeSetCrtc -> DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc); -> drmIoctl(fd, cmd, arg); -> ioctl(fd, request, arg);

Kernel层 : ... -> amdgpu_kms_compat_ioctl -> amdgpu_drm_ioctl -> drm_ioctl -> ... -> drm_mode_setcrtc -> __drm_mode_set_config_internal -> drm_atomic_helper_set_config -> drm_atomic_commit

将修改提交到硬件:drm_atomic_commit 会调用 atomic_commit 接口(设备驱动程序注册到 struct drm_mode_config 对象下的接口),这是厂商自己实现的函数,例如 amdgpu_dm_atomic_commit

atomic display

对一些KMS API增加原子性,比如,mode setting和page flipping操作。这些强化的KMS API称作Atomic Display。

其目的是避免中间状态导致的不一致,确保在复杂的配置中正确的修改模式。

Render nodes

在原始的DRM API中,DRM 设备/dev/dri/cardX同时被特权(modesetting,其他display control)和非特权(rendering,GPGPU computer)操作使用。出于安全原因,打开DRM设备文件需要类似的root特权。这就导致只有一些可靠用户态程序(X server,compositor)才能完全使用DRM API。其他的用户态程序(比如render,GPGPU compute)需要DRM Master的授权才能使用DRM device特殊的授权接口。这种方式存在一个问题,那就是必须运行图形服务器(X server,wayland compositor)作为DRM-Master,即使没有任何图形显示需求,比如GPGPU computer。

render node就是用来解决这个问题,它将DRM user space API划分成两部分-特权和非特权-并使用不同设备文件来表示。如果DRM driver支持render node特性,则可以创建设备文件/dev/dri/renderDX(X是数字),叫做render node,相对的primary node是/dev/dri/cardX。因此,使用GPU的应用无需额外的特权,只需要简单的打开存在的render node就可以使用有限的DRM API。其他需要使用特权操作的程序必须打开primary node采用获取全部的DRM API。