转:https://www.cnblogs.com/shoemaker/p/linux_graphics02.html

1. Framebuffer

  Framebuffer驱动提供基本的显示,framebuffer驱动操作的硬件就是一个显示控制器和帧缓存(一片位于系统主存或者显卡显存)。Framebuffer驱动向应用程序提供/dev/fbx的设备接口,应用程序通过读写这个设备节点实现对显示控制器和帧缓存。

  下面这个程序显示了应用程序操作操作framebuffer节点的过程。运行这个程序,将在屏幕上方显示一个正方形(这里省略了错误检查代码)。

1 #include <stdio.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <sys/mman.h>
5 #include <sys/ioctl.h>
6 #include <linux/fb.h>

8 int main ()
9 {
10 int fd;
11 struct fb_var_screeninfo vinfo;
12 struct fb_fix_screeninfo finfo;
13 size_t screensize = 0;
14 int location;
15 char *fbp = NULL, *ptr;
16 int x, y, x0, y0;
17 int i,j;
18 int ret;
19 
20 fd = open("/dev/fb0", O_RDWR);
21 if (fd < 0){
22 fprintf(stderr, "error open fb0\n");
23 return -1;
24 }
25 ret= ioctl(fd, FBIOGET_FSCREENINFO, &finfo ) ;
26 if (ret < 0) {
27 fprintf(stderr, "get fixed screen info error\n");
28 return -1;
29 }
30 ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
31 if (ret < 0) {
32 fprintf(stderr, "get variable screen info error\n");
33 return -1;
34 }
35 ret = ioctl(fd, FBIOPAN_DISPLAY,&vinfo);

36 if (ret < 0) {

37 fprintf(stderr, "pan display failed\n");
38 return -1;
39 }
40 screensize=vinfo.xres * vinfo.yres * vinfo.bits_per_pixel /8 ;
41 fbp = (char *)mmap(NULL, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
42 if (fbp == MAP_FAILED) {
43 fprintf(stderr, "mapped error\n");
44 return -1;
45 }
46 x0 = 200;
47 y0 = 200;
48 ptr = fbp + y0 * finfo.line_length + x0 * vinfo.bits_per_pixel / 8;
49 for( i = 0; i < 100; i++){
50 char* tmp_ptr = ptr;
51 for(j = 0; j < 100; j++){
52 *tmp_ptr++ = 0;
53 *tmp_ptr++ = 255;
54 *tmp_ptr++ = 0;
55 *tmp_ptr++ = 0;
56 }
57 ptr += finfo.line_length;
58 }
59 munmap(fbp,screensize);
60 close(fd);
61 return 0;
62 }

  应用程序对framebuffer的操作主要是通过ioctl和mmap完成的。mmap将显存映射到用户空间。25行和30行分别获取当前framebuffer驱动的“固定参数”和“可变参数”,这两个参数包含了当前显示控制其的一些信息,可变参数主要是当前分辨率信息,固定参数主要是当前显存的地址。35行FBIOPAN_DISPLAY通常用于双缓存,但是这里使用还有其它意义,在后面讨论drm的framebuffer的时候还会具体讨论。41行将显存映射出来,51-59行操作这片显存,往上绘制一个左上方坐标为(200,200),边长为100的正方形。

  应用程序能够使用这些ioctl是因为内核提供了相应的接口,Linux内核设备驱动相关的书籍都会讨论framebuffer驱动,这里不讨论如何写一个framebuffer驱动。

2. DRM驱动

  前一篇博文的图4内核里面是drm驱动,注意到和图3的区别,单独的FB driver已经没有了,而是被集合到了drm驱动里面。在一些只要求进行进本显示的嵌入式系统上,依然会使用单独的framebuffer驱动,而对于内核中有3D加速的AMD、intel等驱动,内核里面和显示有关的功能已经集合到了drm驱动里面。在AMD/intel显卡+Xorg+3D这样配置的开源Linux系统上,Xorg并不使用,但是系统中仍然有/dev/fb0这样的设备节点,如果我们在桌面环境下“cat xxx >/dev/fb0”,系统不会有变化。然而如果将xorg.conf的Driver修改成“fbdev”,重启Xorg,在执行操作,能够看到有变化(关于Xorg.conf可以参考"man xorg.conf",或者查看这个页面)。或者切换到控制台终端“Ctrl+Alt+Fn”,然后运行同样的命令,就能够看到屏幕上的内容发生了变化,在这篇博文的第一节的程序有一个FBIOPAN_DISPLAY调用,这个调用会使得显卡的Crtc指向的显存地址发生变化而将当前的显示区域切换到fb0设备节点对应的区域,因此上面的程序运行即使在X桌面环境下运行,也能够看到屏幕发生了变化。这提示我们在核外X驱动正常运行的情况下,核外X驱动并不使用framebuffer驱动(实际上drm驱动注册的frambuffer设备只是给内核使用),在X启动运行后,不使用framebuffer 管理的那片内存的内容作为显示输出,而是使用了另外一片内存。(但是:GPU处理完成的图像数据输出到frambuffer。见:https://blog.csdn.net/esc_110/article/details/73300643)

  当前的Linux系统上内核的显卡驱动称为drm驱动,在通常的linux内核发行版上,我们使用lsmod命令查看内核模块,能够看到类似下面的信息:

  radeon                933054  3

  ttm                    45600  1 radeon

  drm_kms_helper         22468  1 radeon

  drm                   162230  5 radeon,ttm,drm_kms_helper

  i2c_algo_bit            5055  2 i2c_gpio,radeon

  机器使用的是AMD的radeon显卡,上面显示了当前系统内核和显卡驱动相关的模块,drm模块是内核drm驱动的基础架构,所有drm显卡驱动都会加载这个内核模块,ttm是ttm内核管理机制,drm_kms_helper是内核模式的基础框架代码,i2c_algo_bit是显卡上操作i2c设备使用的模块,显卡上的i2c设备主要包括了connector,encoder以及pll时钟芯片。Drm内核驱动的代码在内核源码目录drivers/gpu/drm下面。加载了drm驱动后,在/dev目录下面会生成如下设备节点:

  /dev/char/226:0 -> ../dri/card0

  /dev/char/226:64 -> ../dri/controlD64

  /dev/dri/card0

  /dev/dri/controlD64

  其中/dev/dri/card0是操作gpu的接口,发送命令等操作都是通过对这个设备节点进行的。/dev/dri/controlD64是kms相关的设备节点。

  通常核外的驱动程序使用ioctl调用同内核进行交互,linux系统上核外对drm的ioctl进行了一层封装,即libdrm,应用程序通过libdrm调用操作硬件,关于drm的调用,这里有一些比较好的示例代码 https://github.com/dvdhrm/docs,这份代码主要调用libdrm进行模式设置,有详细的注释。

  Linux下的图形驱动的主要部分是核外部分,核外部分包括了xorg exa驱动以及mesa 3d 驱动,exa是传统的2D加速框架,mesa 3d驱动则是针对3D驱动的硬件加速。由于历史原因,在Fedora 16以及更早和稍后的系统上,即使现在硬件上不包含单独的2D部件2D功能是由3D部件实现的,但是2D驱动和3D仍然是分离的。

3. EXA驱动

  早期的2D加速驱动使用的是XAA、KAA架构,但是随着composite扩展的加入,新的exa框架产生了,EXA删除掉了原来2D驱动中的三角形绘制、线绘制等一些现在没什么用处的功能,取而代之的是三个加速功能:矩形填充(Solid)、拷屏操作(Copy/Blt)和混合操作(Composite)。

  “XFree86 server 4.x Design (DRAFT)”文详细介绍xorg驱动需要提供的接口,EXA驱动通过扩展的形式添加并编译到xorg驱动中。在后续的blog文章里面将介绍exa驱动接口,并使用radeon的exa驱动来描述显卡驱动编程过程。

4. 3D驱动

  在linux环境中,我们可以通过glxinfo命令插卡3D硬件图形加速是否可用。比如在radeon显卡上glxinfo的输出包含以下内容:

  OpenGL renderer string: Gallium 0.4 on AMD CEDAR

  这里显示使用AMD CEDAR核心的显卡进行opengl 3d加速,如果硬件加速不可用,则应当是“vmware on llvmpipe”这类字眼。

  开源的OpenGL实现是mesa,mesa向上提供OpenGL接口,下层通过硬件的mesa驱动和硬件交互。GLX是X协议的扩展,用于OpenGL和X的交互(GLX规范)。在mesa源码包中包含了大量的opengl示例程序,包括OpenGL红宝书中的示例代码、调用GLUT或者GLX接口的代码、调用EGL的代码等。

图1

  图1显示了一个3D应用程序运行的过程,OpenGL绘图程序命令被用户空间的mesa驱动翻译成对应GPU的绘图命令放入命令缓冲区中,其他的如顶点信息/纹理信息/索引信息放入到相应缓冲中,mesa驱动为每个应用程序保存了当前的绘图状态,当发生3D程序切换,当前状态被保存下来,当下次调度该程序运行的时候,先恢复该程序的绘图状态到硬件上,然后继续执行命令缓存中的命令。当用户空间调用发送命令到内核的时候,内核驱动对硬件进行编程从该程序的命令缓冲区中取命令开始执行。这个过程是在dri框架下实现的,这里的所有绘制过程并不请求X,OpenGL直接将命令发送给硬件。GLX在初始化窗口,申请buffer和切换buffer的时候才会和X交互。

  图1来自文献“Graphic Engine Resource Management”,这篇文章描述了对早期的内核drm驱动做的一些改进,在这篇硕士论文“A Fair-Share Scheduler for the Graphics Processing Unit对一些问题有更清楚的描述。

其他参考资料:

DRM and KMS kernel modules 描述了drm驱动和kms的一些细节。

最新文章

  1. WPF 自定义BarChartControl(可左右滑动的柱状图)
  2. 面向科学计算的Python IDE--Anaconda
  3. SASS的一些使用体会(安装-配置-开启firefox的调试)
  4. 【循序渐进学Python】2. Python中的序列——列表和元组
  5. Java单链表的实现
  6. HDU4010 Query on The Trees(LCT)
  7. AIX 内存使用情况
  8. KVO监听数组的变化
  9. 并发MD5计算方法
  10. Bresenham画直线,任意斜率
  11. unity与ios交替
  12. iOS 推送问题全解答《十万个为啥吖?》
  13. javascript 原型机制
  14. 51nod 1135 原根 就是原根...
  15. Python快速学习01:Eclipse上配置PyDev &amp; &#39;Hello World !&#39;
  16. 如何通过编译Linux内核打开IPVS(LVS)的debug模式
  17. Linux I/O重定向
  18. 【服务器】Nginx文件配置
  19. [日常] Go语言圣经-GIF动画练习语法
  20. 查看并解除Oracle锁

热门文章

  1. Eclipse 创建 Java 项目
  2. Prime pair connection (Project Euler 134)
  3. python升级后pip 不可用 卸载pip
  4. nginx 日志参数说明
  5. PHP连接MySQL数据库操作
  6. codevs1058 合唱队形==洛谷P1091 合唱队形
  7. EasyNVR支持的摄像机、NVR设备接入类型以及关于国标设备是否支持接入EasyNVR无插件流媒体服务器
  8. Error: member names cannot be the same as their enclosing type
  9. (转)免费天气预报接口API以及全国所有地区代码!!
  10. RedHat6/Centos6.5安装mongodb php driver