【原创】Linux环境下的图形系统和AMD R600显卡编程(1)——Linux环境下的图形系统简介
Linux/Unix环境下最早的图形系统是Xorg图形系统,Xorg图形系统通过扩展的方式以适应显卡和桌面图形发展的需要,然而随着软硬件的发展,特别是嵌入式系统的发展,Xorg显得庞大而落后。开源社区开发开发了一些新的图形系统,比如Wayland图形系统。
由于图形系统、3D图形本身的复杂以及历史原因,Linux下的图形系统相关的源码庞大而且复杂,而且缺少学习的资料(所有源代码分析或者驱动编程的书籍都很少介绍显卡驱动)。在后续一系列文章中,笔者将从对AMD硬件编程的角度出发对部分问题做一个简单的介绍,当然,这种介绍是很初级的,旨在希望能够对初学着有所帮助。
内核DRM、Xorg以及Mesa三部分加起来的代码和整个Linux内核的体量是差不多大的。而且现代的显卡上的GPU的复杂程度在一定程度上可以和CPU相聘美,从程序员的角度看,操作系统(CPU的驱动)包含的许多功能在GPU驱动上都能够找到。比如,GPU有自己的指令系统,着色器程序(GLSL、HLSL、Cg这类着色语言)需要经过编译成GPU的指令集,然后才能够在GPU上运行,符合着色语言规范的3D驱动都包含一个编译器。3D应用程序需要使用大量内存,GPU在进行运算的时候需要访问这些内存,GPU在访问这些内存的时候也使用一套和CPU页表一样的机制。另外,在中断系统上,GPU和CPU也有相似之处。后面的一些内容将会陆续对这些问题做一个简单的介绍。
传统上认为Linux是一个宏内核,设备驱动大部分都是包含在内核里面的,因此可以看到内核代码最庞大的部分是drivers目录,如果从kernel.org上面下载一个内核源码,直接编译,编译的时间大部分都耗在编译设备驱动上。
微内核的操作系统,设备驱动不是内核的部分。这里不讨论微内核和宏内核的区别或者各自的优缺点。但是出于调试方便以及其他一些原因,Linux操作系统上面的一些驱动是放在核外的。一个主要的类别就是打印机、扫描仪这类设备,当前的打印机扫描仪通常都是通过USB 接口连接到计算机上的,对于这些设备的Linux驱动,除了USB核心部分在内核,这些打印机扫描仪本身的驱动都是在核外的。Linux上面的打印机使用CUPS系统,CUPS运行在核外,其驱动是按照CUPS的接口来开发的。Linux上面的扫描仪使用的是运行在核外的sane系统,其驱动是以动态链接库的形式存在的。另外一类核外的驱动是图形系统的驱动,由于图形系统、显卡本身比较复杂,而且由于一些历史原因,图形系统的驱动在核内核外都有,并且显卡驱动最主要的部分在核外。
在wiki词条“Free and open-source graphics device driver”的最新页面上,对linux环境下的图形系统演变过程有一个很好的图解,这里直接使用这个页面的图进行描述 http://en.wikipedia.org/wiki/Free_and_open-source_graphics_device_driver。
显卡最早只有基本的显示功能,可以成为显示控制器(Display Controller)或者帧缓冲设备,对于这样的显示控制器,当前的Linux内核对其的支持表现为framebuffer驱动,Xorg部分对其的支持是一个名为fbdev的驱动。
图1
其后显卡上逐渐加上了2D加速部件,这种情况下面的驱动如上图显示。这个情况下的架构还是比较简单的。
图2
随着3D图形显示和运算的需要,带有图形运算功能的显卡出现,这个时候需要有专门的3D驱动来处理3D,于是出现了上图的GLX driver,这里注意到X server依然处在一个中心节点的位置,无论是X11程序还是3D OpenGL程序,都要通过X server才能到底层。同时注意到到现在为止X的2D driver和GLX driver都是直接调用到硬件,而没有通过内核调用。在2.6版本的内核的系统上,依然能看到X 2D driver中有访问硬件有MMIO和CP两套代码,MMIO就是这里的情况,显卡寄存器直接暴露给核外的2D driver(和GLX driver,目前已经不存在这个了)。
这种情况下的结构仍然是比较简单的,然而这种情况有一个很大的问题,读过X server代码的(或者参考这篇文章“X Window System Internals”http://xwindow.angelfire.com/)应该知道,X server是单线程的(关于多线程X server,可以google到一些邮件讨论记录),main函数完成系统初始化之后就进入了一个循环,等待客户端程序的连入,等一个客户端程序发送完数据之后其他的客户端才能连上来,OpenGL客户端程序每次要请求硬件都必须先经过X server,然后由X server“代表”OpenGL程序进入硬件,对于场景稍微复杂的3D应用程序来说,这样频繁的和X server交互是难以保证3D渲染的实时性的。
鉴于上面的问题,引入了DRI机制,在这个机制下面,OpenGL程序对硬件的请求不再经过X server,而是自己直接和硬件交互。整个过程是这样的:在绘图之前,OpenGL程序向内核申请到一片渲染目标并告诉X server,然后OpenGL程序不经过X server而直接调用进内核请求硬件进行渲染操作,并将结果渲染到申请到的渲染目标中,渲染结束后,OpenGL程序通知X server该渲染目标发生了变化,对应的屏幕上的窗口区域需要进行更新,X server收到这个通知后,进行一次重新的窗口混合工作(当前代码是EXA的Composite功能提供的)。这个时候的情况如下图示,GLX driver被DRI driver取代,和Dri driver交互的不再是X server而直接是OpenGL程序。另外DRI(原来是GLX)不直接操作硬件,而是通过内核drm驱动操作硬件,drm提供硬件访问通道和访问机制。在当前的系统上,2D driver以EXA 2D加速驱动的形式存在于Xorg里面,DRI driver则在Mesa中。
图3
然后又添加了对远程3D client的支持AIGLX,这部分的功能和原来的Utah GLX有类似之处。AIGLX可以参考wiki页面(http://en.wikipedia.org/wiki/AIGLX)。
图4
X server通过不断扩展形成了现在的样子,显得庞大而且复杂,有些工作是不必要的。开源社区提出了更为简洁的wayland图形系统,在“揭开Wayland的面纱(一)(二)”两篇文章里面对wayland做了说明,读者可以点击下面的地址:
http://www.ibentu.org/2010/11/06/introduce-to-wayland-01.html
http://www.ibentu.org/2010/11/06/introduce-to-wayland-01.html
图5
在这两篇文章里面描述了这么一个场景:在一个图形程序上点击某个按钮。在X图形系统下,X的evdev驱动捕获到这个信息,X被告知有应用程序需要进行渲染,然后X server查询到需要渲染的窗口,然后将“鼠标点击”这个事件通知给需要渲染的窗口对应的X程序,X程序接收到通知后再告诉X server“如何进行渲染”,X server接收到“如何渲染”的消息后进行渲染(2D情况下X/EXA自己完成,3D情况下无需先通知X server,使用DRI完成绘制并由应用程序告知X渲染已经完成),完成通知X的“composite”扩展进行混合,X的composite扩展接到混合请求后告知X server混合已经完成,这里面X server和X client以及X server和X composite之间都有双向的通信,这两部分是比较耗时的,而且由于X server通知X composite之前还需要进行窗口的重叠判断、被覆盖窗口的剪载计算等——然而这些是X composite不需要的,因此会有一些额外的时间消耗。
在Wayland情况下,server主要进行composite,应用程序全部使用DRI直接渲染机制,完成后通知Server(composite)进行混合,结构上非常简单而且高效。
另外对窗口的请求也发生了变化,在X DRI下,X11和3D程序需要向X server请求窗口,那个时候X server进行的加速操作必须依赖单独的EXA驱动(无法直接调用OpenGL加速),后来的mesa做了一些修改,在wayland环境下,Application直接通过EGL、GLES driver向内核drm请求绘图窗口,而这里的composite可以直接调用OpenGLES进行加速,而无需单独有2D加速驱动。
还注意到,内核里面有一个KMS,全称是kernel mode setting(内核模式设置,设置显示分辨率之类的),前面曾经提到过,早期的显卡寄存器是直接暴漏给核外驱动的,直到现在的EXA内核代码里面仍然有MMIO和CP两种操作方式,早期X进行模式设置的时候直接写寄存器进行操作,后来内核引入了KMS(在wayland之前就已经有了),于是核外驱动可以通过调用KMS接口进行模式操作而不需要了解硬件细节。
上面提到了composite,X的composite的操作如下图示:
图6
过去的X11 client直接在显示区域上,现在X11 client窗口在off-screen显存上,compositor从这些显存上取出内容并最终混合到显示区域上去。X里面和composite操作相关的扩展有damage、render、composite,这三者描述参考以下资料:
http://cgit.freedesktop.org/xorg/proto/compositeproto/plain/compositeproto.txt
http://cgit.freedesktop.org/xorg/proto/damageproto/plain/damageproto.txt
http://cgit.freedesktop.org/xorg/proto/renderproto/plain/renderproto.txt
关于X composite的引入过程读者可以参考以下资料:
Keith Packard. Design and Implementation of the X Rendering Extension
Matthieu Herrb, Matthias Hopf. New Evolutions in the X Window System
Andy Ritger. Using the Existing XFree86/X.Org Loadable Driver Framework to Achieve a Composited X Desktop
当前的Linux环境上的图形系统架构如下图示:
图7
这里的部分内容在前面描述过了,需要注意的是X server的EXA驱动部分出现了glamor,glamor直接调用到了mesa里面,过去EXA驱动是单独的驱动,通过drm调用操作硬件,而由于mesa的改变,X可以和wayland composite一样直接调用Mesa的接口。
后面的描述将针对Fedora 16操作系统,Fedora 16系统和现在最新的系统(图7)有一定的区别,后面的一些问题的讨论主要使用图4。在后续blog中,将先对Fedora的图形系统内核、X、Mesa这个层次的部分进行一个简单的描述,然后开始介绍驱动对AMD显卡硬件部分模块(功能)的编程。
其他参考资料:
Inside Linux graphics 是intel的开发人员编写的一份介绍图形架构的文档。
Linux Graphics Drivers: an Introduction 是开源社区的开发人员写的一份文档,对Linux图形系统的各个方面都有一些介绍。
A deeper look into GPUs and the Linux Graphics Stack 这一份ppt对图形系统架构有比较深入的探讨。
Graphics Card Interfaces 这篇博文对图形架构的部分细节有一些探讨。
精通嵌入式Linux编程:构建自己的GUI环境 这本书描述了如何在framebuffer上构建一个完整的图形系统,其中的许多概念和原理在X图形系统中也能够看到。