1. 引言

1) 什么是 Graphics?

当我们看到一个东西并在脑子里产生它的样子时,本质上是眼睛感受到了光。

 

所以,图形是光的数字化呈现,形式为图片 (picture) 或者叫帧 (frame),picture 和 frame 本质上是一样的东西。

 

物理世界中的光是连续的,而数字图片是离散的或量化的;

 

数字图片由许多的像素 (pixel) 构成的,如何用数字来表示每一个像素的方式称为 color model,例如包含 3 个 color channel 的 RGB,而具体用多少位的数据来表示某个具体的颜色值的方式称为 color space,

 

color model 是描述了颜色的组成方式,而 color space 则是物理图形世界和数字图形世界的准确链接方式。

 

 

2) 关于图片 (picture) 和像素 (pixel)

下面是关于图片的几个要点:

 

1、图片的尺寸 (dimension,宽度和高度),以像素为单位。

 

2、纵横比 (aspect ratio) 是宽:高的比例,例如 16:9、4:3。

 

3、分辨率 (resolution),像素个数/物理长度,可能是 pixels per inch 或者是 pixels per millimeter,容易和 dimension 混淆。

 

4、根据像素在内存中的存储顺序的不同,显示时也会有不同的扫描顺序 (scan order),例如 linear scan order、raster scan order。

 

5、像素有特定的格式:

 

  • 多个 color channel;透明 (alpha) 通道;Depth (不包含 alpha channel )和 bits per pixel (bpp,包含 alpha channel);像素在内存里可能有不同的组织方式,可能是一个个像素存储,也可能是先存储所有像素的 red channel,再存储所有像素的 green channel,最后存储所有像素的 blue channel;Sub-sampling,多个像素共享同一个 color channel;

 

 

3) 和像素打交道的事情

Graphics 相关

 

Displaying: 根据数码图片的数据,重现相应的光,例如显示器、屏幕、投影仪

 

Rendering: 根据 primitives (渲染时用的术语,其实就是 points、lines 等最基础的元素) 生成数码图片,包括 3D rendering, 2D shape drawing, font rendering 等。

 

Processing: 处理数码图片,包括 Filtering, scaling, converting, compositing 等。

 

Media 相关

 

Decoding/encoding: 压缩/解压缩图片,Picture codecs (JPEG, PNG, etc), Video codecs (H.264, VP8, etc)。

 

Capturing/outputting: 采集或者输出图像,例如 Cameras, DVB 等。

 

2. Graphics 相关的硬件组件

包括 Display、Rendering and Processing 相关的硬件。

 

1) Display 相关硬件

显示输出是通过下面的组件实现,这些组件就构成了一条 pipeline:

 

Frame buffer:一块用于存储像素的内存,可能有多个 Frame buffer,一个 Frame buffer 就对应一个图层 (plane);

 

Display engine:充当 hardware compositer 的角色,负责对图层 (plane) 进行合成,例如将鼠标图层,视频图层、桌面图层合成在一起;

 

Timings controller:当我们已经拥有了一张已经合成好的图片后,我们就需要通过 Timings controller 将像素以正确的时序发送出去。由于都会兼容以前的 CRT 显示器,所以也叫 CRT controller,简称 CRTC。

 

Display protocol controller:当像素能以正确的时序发送时,它们就会到达 Display protocol controller,这里会将像素以某个格式打包,或者叫编码以满足相应的协议,例如 HDMI、eDP、MiPi dsi 的数据包都有各自的编码格式。

 

Display interface PHY:负责物理链 路上信号的发送和接收,例如 HDMI 的 tmds (transition minimized differential signaling),现在一般都是差分信号,并且不断地追求更大的吞吐量。

 

Connector and cable:最后就是通过线缆连接到显示设备,例如显示器、屏幕。

 

在 Soc 的显示模块里,一般都会包含 Display engine、Timings controller、Display interface PHY。

 

2) Rendering 和 Processing 相关硬件

Rendering 和 Processing 有几种不同的实现:

 

GPU(图形处理单元):

 

  • 最常见的实现;高度定制的架构和指令集;专为 3D 渲染设计,也可以做 2D 渲染和处理;通过加载名为 shaders 的程序,shaders 被添加到 的 command stream 后,GPU 会建立 pipeline 以渲染出相应的内容;

 

DSP数字信号处理器):

 

  • 具有专用指令集的专用处理器;

 

固定功能 ISP(图像信号处理器):

 

  • 特定任务的硬件实现;

 

基于 CPU 的实现

 

  • 纯软件实现(经常使用 SIMD);

 

3. 图形相关的软件概念

包括 Display、Render 两个类型。

 

1) Display 相关的软件概念

1、某个时刻,只能有一个应用访问显示设备,不同 app 的显示需求应该被合成一张图片然后再被显示出来,因此需要一个叫 display server 的处理这些事情;

 

2、display server 负责:

 

管理 frame buffer;

 

给 client app 提供一个协议供它们访问显示设备,app 通过这个协议将要显示的内容提交给 display server;

 

协调更新 client app 的 frame buffer;

 

处理输入事件,将事件转发给相关的 client app,例如当你通过键盘输入密码时,你当然是希望只有你正在使用的 app 能接收到你输入的内容,其他 app 无权获得你的密码;

 

将 client app 隔离开来,保护它们的数据安全;

 

3、compositor 是另外一个常见的概念,一般会集成在 display server 中,它负责将各个 client app 的 buffer 合成一张图片,然后将其发送给显示设备。

 

4、window manager 也是一个比较重要的概念,它负责定义 client app 的共存策略,例如哪个 app 位于最上层、哪个应用被选中了、哪个应用正在被移动;

 

2) Render 相关的软件概念

1、与显示不同,GPU 可以并行运行不同的工作;

 

2、GPU 渲染基于 primitives,其实就是点、线之类的东西,这些数据都是 3d 的,但是会被渲染到一个平坦的 2d frame buffer 里;

 

3、还有许多可以被渲染的单元,例如 Texture、Map 等;

 

3、GPU 运行的程序叫 Shader,它们会被添加到 GPU 的 command stream,告诉 GPU 应该渲染什么。

 

4、Shader 有不同的类型,例如负责转换几何大小的 Vertex shader、负责定义颜色和纹理的 Fragment shaders 等

 

4. Linux 的 Graphics Stack

同样分为 Displaying、Rendering、Processing。

 

Displaying Stack

1) Kernel

fbdev 是传统的 display subsystem,目前已接近被完全淘汰,目前在内核里只有 fb console 还在使用它。

 

fbdev 存在许多问题:

 

所有东西都是静态设置、没有 pipeline 的概念、缓冲区是预分配的、不支持热插拔、不支持同步访问等;

 

现在基本都切换到 drm/kms 这套 display subsystem 了。

 

drm/kms 的好处在于:

 

引入了 pipeline 的思想,用面向对象的方式对显示相关的组件进行抽象,将它们封装成不同的对象,这些组件可以独立被配置,并且组合在一起以构成一条完整的 display pipeline。

 

对上层提供通用的 API (DRM uAPI,u 代表 userspace),所有基于这套 API 的显示应用 (一般是 display server ) 可以运行在不同的硬件平台上。

 

另外还有一些好处,例如动态分配 frame buffer、提供 Atomic API 以支持同步等。

 

2) 用户空间的 Protocols 和 Servers

X 是一个历史悠久的 Linux userspace 显示框架。

 

经常被提起的X11 (X protocol version 11) ,其实是一个协议,由于它设计比较早,所以并不是很适应现在的硬件和显示需求,因此有了许多针对 X11 的扩展协议,例如 XrandR (用于动态修改显示配置), XSHM (共享内存以避免拷贝), Xinput2, Composite 等。

 

X11 定义了 client app 如何和 display server 通讯,但是它本身不是 display server ,Xorg 是 X11 的一个实现,所以 Xorg 才是 display server。

 

X 在显示和输入方面使用的是 driver 的概念,例如 xf86-input-libinput, xf86-video-modesetting (用于 drm/kms), xf86-video-fbdev (用于 fbdev)。

 

X仍然是在 server 端进行渲染,并且有许多安全问题和功能限制,因此在 PC 和 嵌入式设备上都慢慢地抛弃它了,替代品是 Wayland。

 

Wayland 是一个更现代的协议,它的出现就是为了代替掉 X,解决 X 所遇到的所有问题。

 

实现 Wayland 协议的 display server 被称为 Wayland compositors,最出名的实现是 Wayland 官方的 Weston,其他的还有 wlroots 的 sway,GNOME 的 mutter,KDE 的 Kwin。

 

为了在使用 Wayland 的设备上兼容 X 的程序,还引入了 Xwayland 兼容层。

 

最后还有一个值得提起的 display server,就是 Android 的 SurFlinger,它是为 Android 深度定制的,基本不可能跑在传统的 Linux 系统上。

 

另外还有一个叫 display manager 的软件,其实就是登陆界面,它会负责启动 display server 以启动桌面环境。在嵌入式系统中,一般都不需要 display manager。

 

3) 用户空间的 Libraries、Toolkits

下面会列举一些与图形相关的库、toolkits、或者是桌面环境。

 

实现 display server 协议的底层库:

 

Wayland: libwayland-client, libwayland-server

 

X11: XCB, Xlib

 

上层应用不会直接使用这些库来开发应用,而是使用 Graphics toolkits。

 

用于编写 GUI 应用的 Graphics toolkits:

 

GTK (C): Widget-based UI toolkit,支持 X 和 Wayland;

 

Qt (C++): Widget-based UI toolkit,支持 X 和 Wayland

 

EFL (C): Lightweight UI and application library

 

SDL (C): Drawing-oriented graphics library,一般用于游戏开发,虽然它算不上是一套 toolkits,但是它和其他 toolkits 起的作用是差不多的,都是辅助开发者开发图形应用。

 

有了 Graphics toolkits 就可以开发出一套完整的桌面环境了,一般会包括::

 

一套基础的 app;

 

Window manager 和 compositor;

 

最后一个要提到的库就是 libdrm:

 

它是对 DRM uAPI 的封装,它会访问 kernel space 里的 DRM driver。

 

事实上,如果你不是要写 display server,或者做一些特别硬件相关的活的话,基本不会直接基于 libdrm 写程序,甚至很多内核里写 DRM driver 的驱动工程师也不是很熟悉 libdrm。

 

Rendering Stack

1) Kernel

在 kernel space 里,DRM 同时也负责管理 GPU。

 

但是和显示不同,在应用层并没有一套通用的 API 来访问 GPU,即对于不同 GPU 的 DRM driver,都会提供自己特有的 API 给应用层。这是因为不同厂商的 GPU 的实现都太硬件相关了,很难设计一套统一的 API 来使用这些 GPU。

 

通常的情况是,在 kernel space 里,GPU driver 只实现最底层的 io 相关功能,例如使用 DRM GEM api 对 Memory buffers (或者叫 buffer object,简称 BO) 进行管理、command stream 的验证与提交,而更核心的活都留给 userspace libraries (例如芯片厂商提供的 libdrm 和 libGLES 之类的,或者是开源社区提供的 Mesa3D ) 去实现。

 

2) Userspace APIs for 3D

芯片厂商提供的 GPU userspace libraries 向下会访问 GPU,向上实现通用的 API 以便上层应用进行 3D 渲染,目前常见的 3D 渲染 API 包括:

 

OpenGL,用于 desktop GPUs,使用 GL shading language (简称 GLSL) 作为其编程语言,其 shader 的格式为 glsl。OpenGL,用于的特点是在易用性和功能性方面比较均衡,以不太复杂的方式提供了常用的 GPU 访问操作。

 

OpenGL ES,用于 embedded GPUs,OpenGL 的子集,但是其实一般都会同时支持 OPenGL 和 OpenGL ES。

 

EGL,OpenGL 和 OpenGL ES 都是只负责渲染 (即使用 GPU 生成图像),但是还有另外一个重要的活就是如何将图像从 GPU端 导到 Graphics 端,这就是通过 EGL 这个 api 来实现的。它负责协调 render 和 display,具体的就是提供一些内部会使用 openGL 的函数,然后通过 X / Wayland 协议将 buffer 提交给 display server。

 

Vulkan,一种更新的 API 的。它提供了更多 GPU 的高级用法,它涉及更多以及更细粒度的 GPU 底层操作,因此要编写的代码也更复杂。另外,Vulkan 有自己和 display stack 交互的方式,称为 Vulkan WSI (Window System Integration),因此它不再需要使用 EGL。Vulkan 不仅仅能用 GPU 进行渲染,还能使用 GPU 进行计算。Vulkan 的 shader 格式为 SPIR-V。

 

3) Userspace Implementations for 3D

上面说的是 API,而具体的实现一般有两种形式:

 

芯片厂商提供的闭源实现,以动态库的形式提供;

 

开源社区提供的实现:Mesa 3D,有源码。

 

我们重点了解一下开源的 Mesa 3D。

 

https://docs.mesa3d.org/

 

Mesa 3D 的特性如下::

 

支持 OpenGL, OpenGL ES 和 Vulkan APIs;

 

支持有 DRM render driver (或者叫 DRM userspace driver) 的 GPU,包括 radeon, amdgpu, nouveau, etnaviv, vc4/v3d (树梅派的 GPU), lima (ARM 的 Mali-4XX 系列), panfrost (Arm 的 Mali-TXXX / Mali-GXX 系列);

 

支持 software render,包括 softpipe, swr, llvmpipe, lavapipe;

 

支持 GPU video decoding,使用 VDPAU、VAAPI、OMX api;

 

支持使用 OpenCL 进行计算,目前只支持 clover driver (intel);

 

4) Libraries for 2D

再来看一下 2D 的渲染,我们即可以用 CPU 进行 2D 渲染,也可以用 GPU 实现 2D 渲染。

 

相关的库:

 

cairo: 一个广泛使用 drawing library;

 

Skia: 谷歌提供的 drawing library;

 

FreeType: 传统的矢量字体渲染 library;

 

HarfBuzz: 比较新的矢量字体渲染 library;

 

Processing Stack

这一块我也不是特别懂,但是为了内容的完整性我就进行简单的介绍吧。

 

对于 Processing,会有下面几种实现:

 

使用优化的基于 CPU 的算法;

 

使用特定的 SIMD CPU 指令(NEON、SSE、AVX);

 

使用 GPU;

 

相关的库:

 

用于像素格式转换和缩放 FFmpeg libswscale;

 

用于各种像素操作的 Pixman;

 

用于 NEON 加速像素操作的 ARM's Ne10;

 

用于快速傅立叶变换的 FFTW;

 

图像处理框架 G’MIC ;

 

到此,关于 Linux 图形显示的框架就介绍完毕了,感谢阅读!