• Linux环境下的图形系统和AMD R600显卡编程(2)——Framebuffer、DRM、EXA和Mesa简介


    转: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 ");
    23 return -1;
    24 }
    25 ret= ioctl(fd, FBIOGET_FSCREENINFO, &finfo ) ;
    26 if (ret < 0) {
    27 fprintf(stderr, "get fixed screen info error ");
    28 return -1;
    29 }
    30 ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
    31 if (ret < 0) {
    32 fprintf(stderr, "get variable screen info error ");
    33 return -1;
    34 }
    35 ret = ioctl(fd, FBIOPAN_DISPLAY,&vinfo);

    36 if (ret < 0) {

    37 fprintf(stderr, "pan display failed ");
    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 ");
    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的一些细节。

  • 相关阅读:
    flutter添加启动图及设置启动时间
    flutter中通过循环渲染组件
    flutter学习资料汇总
    flutter中显现登录页面成功后跳转的方法
    flutter 常用视图组件
    mpvue学习笔记
    按钮放大动画效果
    一位练习时长两年半的内网渗透练习生
    Kali系统中20个超好用黑客渗透工具,你知道几个?
    渗透测试之三内网跳板
  • 原文地址:https://www.cnblogs.com/newjiang/p/9561523.html
Copyright © 2020-2023  润新知