(1) 本文会尽量从初学者的角度去描述整个Linux整个图形子系统,但由于其复杂性,涉及到的模块比较多,可能会需要一些相关的先验知识;
(2) 对于系统的介绍,分析的着重点可能不会在于为什么该这样设计,而是在于在现有的显示系统下,我们能做些什么来适配我们的目的;
1、前言
GUI作为人机交互信息量最大的一种方式,无论在消费还是工业级产品上都大行其道。但同时由于它处在整个系统的核心位置,对外需要通过鼠标、键盘 、显示器进行I/O获取和控制,在内需要负责图形的生成,渲染,整个系统复杂度比较高。本文会从以下几个 方面来介绍GUI子系统:
- 以Linux下的原生GUI子系统为例,概述GUI子系统的概念,软硬件部分在GUI子系统的中的角色及大致构成;
- 着重分析Linux下DRM+KMS的软件实现方式,并且以Xilinx的Zynq-7000 SOPC为例,详细介绍相关片内硬件模块在GUI系统中的角色及实现方式;(由于GPU模块硬件的源码的开源程度不高,不在本文的分析范围内)
- 分析现有GUI框架下,在硬件加速方面,我们能做的事情,并以非常简单的图像处理为例,给出相应的设计方案;
- 本文的最后一部分,会在Zynq-7000上面(digilent的Zybo开发板)部署整个Linux+硬件边缘提取处理+Qt+HDMI的环境,并给出具体的实现流程;
2、Linux GUI子系统概述
GUI作为人机交互的一种方式,通过其承载的大量信息提高了信息交流的效率。这里我们不介绍鼠标、键盘等输入设备,只介绍输出显示这一子模块。生活中大家最常见的图形化界面估计就是图像化界面的桌面环境,即窗口系统,(如下图的Ubuntu、Xfce等)。
窗口系统一般都具备以下基本功能:
- 通过WIMO(Window-视窗、Icon-图标、Menu-选单、Pointer-指标)4个基本元素来实现人机交互;
- 上述的4个基本元素都能通过第三方的程序来扩展(也就是安装新程序);
在实现方式上,大部分Linux下的窗口系统都是通过X来响应不同的交互请求及输出到显示器上。因此,整个应用层的GUI结构如下:
因此,在应用层面上,GUI系统的核心部分是X,X的总体功能一句话描述如下:通过指定的协议接受本地或远程的鼠标、键盘需求,并切输出相应的窗口画面到显示设备上。细分来讲,X主要由以下4个组件构成:
- X server:负责软硬件的管理,将输入的软硬件事件通过一定协议转发给X client,将输出的图形绘制在屏幕上;
- X client:每个需要涉及到GUI的App,可以实例化为一个X client,X client主要是响应X server分发下来的事件,通过处理后,将待绘制的图像回传给X Server;
- X window manager:X window manager作为一个特殊的X client,主要负责为X server管理多个X client(一个具体的例子就是对虚拟桌面的管理),起着视窗管理员的角色。常见的X window manager如下:
- GNOME (GNU Network Object Model Environment)
- KDE (K Desktop Enviroment)
- twm (Tab Window Manager
- XFCE (XForms Common Environment)
- Display manager: 提供登陆许可环境以获得X Window的控制;
我们再从开发者的角度来看一下GUI。以Qt为例,我们在使用Qt组件进行开发时,一般是利用组件中的各种类库,去响应各种事件输入(单双击鼠标、键盘操作)以及给出相应的输出到显示器上。其实际工作的时候,这些工作底层都是通过和window system(X)之间的交互实现的。
这些基本事件的响应,基本的图像单元的绘制,是window system通过封装成一个通用的GUI工具集提供给QT(如X的xlib)。对于Qt而言,这个window system可以是X,也可以是QT自行研发的QWS视窗系统。整个应用层的GUI系统则可看作如下:
3、Linux GUI子系统的构成及工作流程
从应用层深入到内核中去。暂不考虑在linux下的GUI,我们知道,单纯的显示图片的话,整个数据流的走向是这样的:
即按照一定时序时序,将图像信息从内存中输出到显示接口上。若在生成Frame buffer里面的图像数据时不仅通过软件memory处理,还用到了硬件加速的话,数据流则变为如下:
其中accelerate logic就是显卡部分(若是SOC的片内GPU模块,则是通过片内高速总线进行数据交互的,若若是独立显卡,一般是通过pci-e高速串行接口进行数据传输的)。把这个数据流走向放入Linux中,数据流和控制流都需要和用户层进行交互,也就是说,Linux下,必须得有相关的软件驱动给用户层提供相应的API。这也就是DRM(Direct Rendering Manager)和KMS(Kernel Mode Setting)的角色。
Linux原生系统中提供由DRM+KMS构成的DRI(Direct Rendering Infrastructure)中:
- DRM主要负责负责数据流,即通过软件或硬件,生成目标图像,存储在framebuffer中;
- KMS主要负责控制流,即针对外置LCD以及指定的显示模式设置,将生成好了的frame数据信息送到响应display port上(VGA、HDMI等);
Kernel将这两大快的基本API抽出来封装成libdrm供X使用,整个应用层+kernel相关的GUI结构如下图:
整个data flow也替换成了上图的flow1~flow6。关于DRM和KMS的详细介绍我们会放到这个系列的第2篇,这里再提一下涉及到3D的GUI。在需要用到3D图形交互的场景,往往对着实时性要求较高,X中的server/client之间的数据协议解析以及数据交互导致的延时是这种场景不能容忍的。因此DRI是支持这种app越过X直接和内核交流的方式的。比如,Qt中可以直接通过opengl相关类库直接调用libdrm中API控制硬件中的Frambuffer软硬件,此时结构如下:
4、我们能做些什么
在一个常见的系统研发中,子系统中我们能做的基本就是适配,适配不同的CPU、适配不同的OS、适配不同的显示设备。而对于专业的GPU研发团队来说,则需要在现有DRI框架下,为自己的GPU逻辑设计专用的驱动,软硬件工作量庞大。作为一个高性能计算实验室,当然要将一些高速计算融进去。在本系列第3篇,我们将会在Xilinx的Zynq7000系列芯片上,利用其中的PL逻辑资源,设计非常简单的图像处理IP,加速DRM中的Framebuffer数据并通过HDMI显示到LCD上。
相应文章会同步更新至专栏中