翻译:Linux Graphics的现状

原文地址https://sites.google.com/site/jonsmirl/graphics

英文原文:The State of Linux Graphics 
作者:Jon Smirl 
时间:2005 年 8 月 30 日


在退出Xgl之后,我收到很多邮件,也读了很多文章。我的结论是很多人都不知道Linux Graphics是怎么回事。想到人们并没有一个整体的把握,这种状况就不难理解。Graphics是一个庞大而复杂的领域,拥有众多的软件构件和完整的开发组织。我撰写这篇文章,正是为了解释这些构件如何协同工作。

历史

今年(2005年)是 X server 21岁的生日。从一个1984年的 Athena 项目开始,X 就开始为Unix社区服务。X在今天已被Linux桌面广为使用。这篇Wiki百科提供了开源X的两种运作方式,包括其对跨平台性和网络透明性的支持。

然而,X 已经使用了21年,视频硬件也更新换代。如果你看一下现如今的显示芯片的平面图,你会看到一个被标为 2D 的区域。这是因为 90% 的芯片被设计成 3D 绘图通道,你若买了一个只有 3D 通道的硬件,它就不能很好的支持桌面环境的工作。不过许多人都没有意识到,3D 硬件可以给 2D 桌面绘图。看看你的屏幕,他是 2D 的,不是么?任何 3D 绘制的图像,最终都要显示在 2D 屏幕上。因此,只要进行适当的软硬件编程,3D 硬件就可以给 2D 桌面绘图。

显卡厂商

多年以前,显卡芯片厂商很乐意给出他们产品的数据表和编程信息。但是随着专利库的增加,专利侵权也随之上升。现在芯片厂商都会以防止侵权的借口,隐藏掉所有的芯片信息。至少厂商们隐藏了编程规范。他们声称隐藏编程规范可以防止竞争对手对芯片逆向工程,但是我仍持怀疑态度。所有这些隐藏的规范给撰写开源代码带来了麻烦,转而我们必须等待厂商自己提供新设备的驱动。假设你还没有发现,其实芯片厂商仅关心 MS Windows,所以他们对 Linux 仅提供了最小化的支持。有个特例。Intel 为一些芯片提供了部分芯片的开源代码。Nvidia/ATI 提供了闭源驱动,但远落后于 Windows 版。最终的结果是,大部分显卡驱动都处在低质甚至找不到驱动的范围。

与其他桌面的对比

其他桌面,如 Windows  Mac ,都支持 GPU 加速。显然,有比没有的显示效果好。某些情况下,前者要比后者快100倍。长远来看,芯片厂商可能会移除2D模块,只保留3D模块。Microsoft 和 Apple 似乎得到了这个消息,即3D是趋势并在努力转型。可相反,X的贡献者们,却告诉我们不要去讨论竞争格局,因为他们只想把X变得更好。可是,X开发者们没有意识到,Redhat和Novell的产品经理可不这么想,尤其是他们的系统GUI因缺乏竞争力,而销量锐减以后。

使用 3D 绘制桌面,不仅仅是更养眼的问题。3D 处理比 2D 处理更快,没有人再去试图让 2D 函数变得更快,所有芯片设计都在向着 3D 发展。举些例子,你可以加速,如图像的色域的变换,拉伸/缩放等过程。我已亲眼见到一些极为复杂的图像处理,在硬件着色器的帮助下几乎实时完成,而换做CPU则需好几秒。它还支持错杂的色彩深度(同时存在8,16,24-bit 的窗口)随意转换。此外还能为放映机提供急速的屏幕翻转/旋转,和为视觉障碍用户提供急速的全屏幕缩放。分辨率的独立允许物体以任意的分辨率、尺寸、大小、方向在屏幕上输出。而剩下许多有趣的应用会在接下来描述。

通用的 X.org Server

X.org 就要发布 X11R7 了。这一版的主要特征是将核心代码模块化。然而模块化对普通用户没有任何意义,它只是使X编程变得容易。模块化之前,X代码树是个16兆行单个项目。模块化之后,它被分成了多个独立部分,使项目更容易构建和理解。

X,这个操作系统

X属于应用还是属于操作系统?这有个不错的引用,关于X server各组件如何协作的。它有8年了,但仍然中肯。如果你不知道 DIX、mi、DDX、CFB 等含义的话,请阅读它。从 X11R6.3 到 Xfree86 开始,一些地方变得不同。X server设计变得越来越可跨平台。众多被X纳入考虑范围的操作系统,X对其有着不同层次的支持,比如硬件检测。为了给这些功能分配物理地址,X增加了检测 PCI 总线的代码,以方便寻找硬件,如启动/重置视频 ROMs 的代码,查找鼠标/键盘和其它支持设备的代码,管理多 VGA 设备和平衡自身模块加载器的代码。现况是,X属应用还是操作系统,界线很模糊。当X的一些操作环境亟需类似操作系统功能支持的时候,它们却在操作系统中作为服务实现,这才造成了今天 X 与操作系统之间的冲突。当然,操作系统若能在十年前高瞻远瞩的话,这个问题也就不会像今天这样令人烦恼了。

Linux 内核提供了 PCI 和帧缓存( framebuffer )两个子系统,这是 BSD 等其他平台所没有的。在跨平台要求的指引下,X server构建了这两个子系统的并行实现。在过去,这些X server中的子系统是必需的,但是在 Linux 上却导致了两个不同程序控制同一个硬件的情况。Linux 有多用户非图形界面级别(run level 3),即硬件与X交互的唯一方式是通过内核。内核已经提供了大量用户协调机制,如 PCI 子系统,输入设备,热插拔检测,和设备驱动。考虑到对 Linux 的协调,最好的解决办法是使用内核功能,在其他平台上使用 Linux 上复制过去的库。Fbdev 和 XAA 驱动,这两个范例,就是借此目的而实现的复制驱动。

Linux 有极好的热插拔系统,并且图形系统真心该用它。屏幕可能以多种方式热插拔( hotplug )。以往,你可能只需要拿着新显卡插入插槽。但也可能不同以往。另一个人也许正使用屏幕,而你刚好将这个屏幕加入了你的用户组,当他登出时,你的屏幕却被迫挂起。你不得不给你的笔记本链接一台外接显示器,来处理此次热插拔。此外,你可能会用到 DMX Chromium 等工具无线连接投影荧幕。USB 也是经常热插拔的设备。用户可能热插拔鼠标、触摸板、键盘、声音设备甚至图形适配器。而X server并未直接管理任何一个上述设备,因为它不直接与热插拔系统打交道。

一个标准Linux桌面用户使用提权( root )的 X ,这就意味着 X 控制了最高权限的桌面绘图,所有窗口都基于 X 。Cygwin/X、Directfb 和 Apple Darwin 都使用未提权的 X 。这些环境中,其它部分来负责显示,比如窗口管理系统。窗口管理系统主要绘制桌面,并实现了相应的窗口管理系统的 API 。 X 可以整合在未提权的窗口管理系统中。在未提权的 X 下, 应用窗口把图像绘制在系统内存缓冲区中。在适当的时刻,窗口管理系统会同步窗口并显示。未提权的 X 也提供了环境与设备之间的,鼠标键盘事件响应协议。

X Render

X Render这个扩展。在2000年,KeithP 针对 X Server 不能有效绘制清晰反锯齿文本的问题,想出了一个X Render扩展的办法。X Render是 X 的核心构件,它允许在X Server,实现如美化字体和 Cairo 的功能。X Render在X Server中添加了Porter-Duff操作。这些操作允许图层以不同方式结合。这就好像OpenGL的纹理( texture )和纹理操作,只是相似但并不重合。

XXA

XXA,X Acceleration Architecture 的简称,在 XFree86 4.0 中引进。为实现跨平台,XXA在没有内核设备驱动支持的情况下,实现了2D显卡驱动。用户空间的驱动可以工作,但是由于脱离了内核驱动,Linux 内核不能跟踪 X 的硬件操作。X 直到系统启动后,才能启动。而你可能会遇到 X 的启动错误,需要显示 X 之前的启动过程,不是么?Linux 显示启动过程,需要文本模式(终端console)的驱动,最普遍的就是 VGAcon。所以在Linux下你必须使用两个设备驱动,X 和 console,这两个都想控制同一个硬件。XAA 与 console 乃至 Linux 虚拟终端的结合,会导致冲突,之后解释。

EXA

EXA 替代了原有的 2D XAA 驱动,并且延长了旧有的服务模式。它利用更高级的驱动和内存管理 ,提升了X Render的性能。EXA最初是作为所有硬件包括旧硬件的解决方案而提出的。但这是不现实的。如果旧硬件没有提供Render 硬件加速支持的话,那么什么都做不了。尽管还有一种可能性,EXA 可能利用更好的显存管理,能提升这些芯片的性能。所以 EXA 做了很多我们本可以让 OpenGL 做的事。当然这有特例,比如nv和i128,这些支持 3D 技术却不支持 OpenGL 标准的驱动。当然还有一个好处,EXA 极大的扩展了显卡芯片的 3D 支持能力。对 EXA 的修补会持续一段时间,但是不会很久,毕竟我们总是想要更多更好的构件。

Cairo

Cairo的目标是提供一个高质量的 2D 绘图 API,以供打印和显示。它在设计时,兼顾了X Render,并且它实现了 PDF 1.4 的图像处理模式。出于对Stroking(译者注:3D Stroke?轨迹线?)和贝塞尔样条曲线、构造转换透明图片、抗锯齿文本渲染的考虑,Cairo有着轻便的设计,可以嵌入绘图后端,如 image、glitz、png、ps、PDF、svg、quartz、GDI 和 xlib。Cairo 已经开发两年了,并且也看到上游 GDK 和 Mozilla 已经部署。Cairo 的主要目的是提供显示屏上高质量的末端2D绘图,和其便捷打印。这个可热插拔的末端绘图工具,允许应用程序使用相同的代码绘图和打印。

Cairo 的末端之一,glitz,用 OpenGL 实现了 Cairo。这有一些资料详细的解释了 glitz。自从 Cairo 实现了 xlib 和 OpenGL 两个后端,一个性能的直接比较就在 xlib 和 OpenGL 之间展开。在 Benchmarks 测试中,OpenGL 以 10 倍至 100 倍的又是打败了 XXA。这个超高的性能表现归因于 glitz/OpenGL 对绘图芯片 3D 硬解码的使用。比较 Cairo 在 glitz 与 xlib 上的差别,也更好的说明了 3D 硬解对 2D 绘图高表现。

DRI和OpenGL

DRI,Direct Rendering Infrastructure,实现了OpenGL与X server的协同工作。DRI有四个主要组件。第一,LibGL 实现了 OpenGL 这一标准的接口,并可以在不同显示芯片上切换。其次,还有一些针对特定硬件进行编写的 DRI 库。由于 OpenGL没有被 DRI 驱动支持,你需要软件上进行回退(fallback)实现。回退被 Mesa 支持。注意 Mesa 是 OpenGL 在软件上的完整实现。一个没有硬件加速的显卡,仍然可以回退到 Mesa 以实现所有 OpenGL 函数。最后,DRM,直接渲染管理(direct rendering manager),在内核中运行,管理硬件并且所需的安全保护。

DRI 有趣的一点是,这个D(direct)。每个直接使用 DRI 的程序,都把它当做视频硬件对待。这不同于 X 的特性,你需要发送绘图命令给服务,然后服务程序在硬件上帮你绘图。DRM 内核驱动协调多用户以阻止冲突。这种模式的好处是,OpenGL 可以减小进程转换和向控制端数据传输的开销。不足是图形卡会处理大量的上下文切换。工作站卡似乎可以减少切换工作,但是一些消费级显卡不能。Microsoft 已经陷入此问题的困扰,并且要求硬件支持 DirectX 10 ,以支持更高级的上下文切换。

DRM 同样实现了带有主用户和普通用户的多用户支持。这种支持允许用户初始化显示设备和控制 GPU 资源的消费。以 Root 权限运行的 X server,作为 DRM 的管理者(主用户)运行。DRM 管理并不依赖 Root 权限,这儿有一个补丁移除了这个依赖。

当硬件缺少某个构件时,Mesa 会以软件的方式实现这个构件。这被称作软件回退(software fallback)。人们对此感到困惑。他们说 OpenGL 没有完全加速但是 X server 做到了。想一想。两个驱动运行在相同的驱动下。如果你在两者构件中都调用了驱动 APIs ,那么他们就都加速了。如果没有,编程修正适合的驱动吧。

安全和 Root 权限

Linux 内核包含 10M 行运行在 Root 下的代码。X server 包含 16 M 行的多数运行在 Root 下的代码。对于安全漏洞,你认为哪个更容易发现?没有技术理由要求 X server 运行在 Root 下。提及跨平台能力,当前的 X server 运行在 Root 下,是为了让用户空间使用视频硬件。Linux 对此已有解决方案。你可以把特权代码放在设备驱动中,非特权代码放在用户空间运行。特权驱动代码平均 100KB 。这远远小于 16M 行代码。

老一些的设备

任何视频硬件,甚至不为人知的 VGA 适配器,都能够运行 X server,并且 Mesa 会幸运的以软件方式实现整个 OpenGL API。问题是速度有多快。把一个 VGA 适配卡转成一个 ATI X850(译者注:04年的 ATI 旗舰显卡)并不需要大量编码。老显卡可以被当前的 X server 很好的支持。但是新系统是围绕新显卡设计的,他们可能在老显卡上表现不佳。你也许想要考虑升级你的视频显卡,图形卡,下降的 OpenGL 解码能力,可能花费你40美刀并且表现更差。另一种选择是,你可以不去升级,继续运行过去表现良好的驱动软件。

内核绘图支持

当内核第一阶段启动时,你会看到启动终端。在 x86 机上,最主要也普遍的启动终端是 VGAcon。VGAcon 使用了遗留下的图形卡的 VGA 硬件图形支持。由于绝大多数 x86 图形卡支持 VGA,VGAcon 为此平台提供了一个通用终端。在非 x86 平台上你可能没有 VGA 硬件图形支持。在这些平台上,你通常加载芯片特定的帧缓存驱动。内核提供了庞大的 fbdev 驱动,以致大量的硬件都被支持。

VGA 遗留的问题

IBM PC 的原始设计中,或多或少有一套限制外围设备的设计。它限制众所周知的固定地址访问,如 COM1 的 0x3FB。不幸的是,在大多数显卡上的 VGA 支持,是需要访问固定地址的遗留设备之一。只要你系统中有了一个图形卡,VGA 不是个问题。可再接一个 VGA 设备的话,你就会有两个硬件同时想要占用相同的总线地址。

当前的 X server 有代码去解决多 VGA 适配器的问题。但是这段 X server 代码不知道设备的其他用户,并且他会把多用户规则践踏个遍。破坏它总不是办法,所以我们协调 VGA 设备多用户的解决办法。在 Linux 上,最好的办法是把 VGA 仲裁机制加到内核中。BenH 做了很多,已在 OLS 讨论的工作

另一个问题是多显卡的初始化。显卡有 ROMs,被成为VBIOS。它通常在启动是初始化硬件。在过去的设计中,许多初始化都支持 VGA。由于过去我们只能使用一个 VGA 设备,系统的 BIOS 只会初始化第一个视频设备。当然可以在较迟的启动中初始化第二块显卡。我们可以用x86虚拟器的方式来虚拟另一块 VBIOS,以此来与一个 VGA 设备协同工作。为了是问题得到改善,这儿有两种普遍的 ROMs 格式 – x86 编码和 Open Firmware。由于ROMs 的卖方负责了大量的 Open Firmware 模块,在非 x86 设备上使用 x86 显卡变得容易。为使 VBIOS 的相关程序在 x86 上运行,BenH 做了大量相关工作。

Fbdev,也称framebuffer。在 Linux 下,framebuffer 主要负责:初始化硬件,检查显示设备,检查可用的模式,设置输出模式和配置信息,硬件光标,色彩映射,向上的底层接口,显示设备的挂起和恢复。内核中有大量的 framebuffer 驱动,并且在他们的构建中实现了多状态的支持。考虑到 fbdev 接口是基于内核的,并没有任何来自用户空间的带有Helper操作器的应用能够停止 fbdev 支持。带有 Helper 操作器的应用能够操作 VBIOS ,当源代码无效时。

当你运行一个 VBIOS 是,它会创建一系列低层级的访问点,这些访问点可以用来控制显示。这些内容非常具体,并且我不能解释在 VGAInt10  VESA 之间的所有不同。总的来说,VGA是 IBM 基于硬件寄存器标准建立的。Int10 使用了软件中断处理显示的。软中断只在 x86 实模式下工作,并且除了类似 grub 之类的的东西,基本不被使用。Int10 的代码来自于 VBIOS ,并且随着 ROM 的运行而初始化。系统中只能安装一个 Int10 驱动。VESA 掌管了 Int10 模式,并且拓展了它在保护模式下的工作。当编译内核时,你会看到三种驱动:VGA、 VESA fbdev 和 VGAcon。VESA fbdev 需要 fbconsole 的读取才能运行,VGAcon 需要运行所有这些。所以在 非x86 上,通常会同时看到 fbdev 和 fbconsole。

支持多用户

Linux 是多用户操作系统。它就是那样工作的。但是过去连接多用户的唯一方式,已经发展超越了串行电缆和网络。如果你在本地安装了多显卡,运行了多用户,会发生什么?它不能工作。有些补丁试图解决这个问题,例如 Linux 终端项目 X 上的探索。问题是 Linux 控制台的虚拟终端系统,只支持本地显卡和单用户。考虑到 fbdev 可以支持多用户,正是虚拟终端才是多用户的阻碍。

一同努力的企鹅们 本地多用户在很多场合都有应用,比如学校、监控系统、联网的咖啡店,甚至在家。过去对多显卡支持的要求,不像现在这么高。这归因于 PC 机普遍只有一个 AGP 插槽。多PCI 显卡确实工作,但性能不高。有些高端设备有多个 AGP 插槽,但是比较罕见。PCI express 改变了这个局面。所有 PCI express 插槽工作起来是平等的。唯一的变量是每个插槽有几个通道。硬件的改变使得利用多个高性能显卡构建系统变得容易。PCIe 计划在单系统内支持最大16个显卡。

正在分裂的控制台

如果你分析控制台系统,你就会注意到控制台所使用的两个不同的类。系统控制台,提供了启动显示和错误报告、恢复和维护。用户控制台,用户登录后的正常显示,提供了命令行活动和编辑。在当前的 Linux 系统中,两种控制台被同样的控制台代码服务。

分离控制台代码的可能取决于使用类型和修复 Linux 控制台的现存问题。首先,系统控制台应当完全可靠,防攻击。它不需要运行的很快。它需要尽可能用最简单的代码运行,并且它需要工作在中断时内,来源于内核错误中。使用场景包括系统恢复和单用户模式。启动时的显示和内核调试支持也是可能的。系统控制台会提供一个支持安全留意键(Secure Access Key)和安全登录的屏幕。为了支持每个独立用户登录到其显示器对应端口上,控制台需要对每个显卡的接头独立地实现。一种访问新系统控制台的方式是按 SysReq 键,控制台会推迟当前显示,并且使用当前的视频模式。Novell 内核调试器就是以这种方式工作的。系统控制台不支持虚拟终端,和控制台切换。因为它了解你的视频模式,需要能在紧急状况下占据你的显示,比如,一个严重的 kernel OOPs 错误。

系统控制台的设计,使用了 fbdev 去跟踪运行模式,和显示缓存(scanout buffers)的地址。为了是它尽可能的可靠,所有加速支持都被移除。帧缓存控制台(Fbconsole,framebuffer console)然后使用系统 CPU 直接掌管帧缓存。控制台的显示,是通过在显示缓存中直接绘图完成的,显示缓存使用了帧缓存控制台支持的点阵字体。

与系统控制台不同的是用户控制台,它需要更高的性能和对用户的友好。在用户空间实现,使得它更容易管理多用户,只需通过为每个用户创造一个进程即可。用户空间允许通过 fbdev 和 DRM 作完全加速。你也可以轻松访问 Xft/FreeType 以实现全 Unicode 编码的支持。良好的设计甚至让控制台在一个显卡接口上支持多个用户。通过编写合适的快捷键,它可以模拟已成型的虚拟终端,支持控制台之间的切换。由于当前两种控制台结合使用,你在虚拟终端中切换时,两种终端都会遇到。在当前新的虚拟终端切换快捷键,会切换到用户终端。SysReq 键会激活系统控制台。Shell 进程与系统控制台紧密联系,高优先级使得 Shell 很容易从一个错误进程中重新抢占控制权。

分门别类的硬件

本地多用户支持表明,Linux 会实现一个控制台群组的概念,这个群组是由面向用户交互的外围设备组成。控制台群组是硬件集合,它们共同组成了一个登录控制台。一个例子,群组可以包括显示器、鼠标、键盘和声卡。在登录时,可插式认证模块( PAM, Pluggable Authentication Modules ) 会登记这些设备的所有权给登录用户。与此相关的一个有趣的附属说明,是 USB 集线器也算在控制台群组的一部分。当用户登录后,任何插入 USB 口的设备都将属于该群组。登出后所有设备都将返回未登记池( unassigned pool )。

可选的替代品

这些小部件有:directfbsvgalib、[fresco(http://www.fresco.org/)、Y WindowsFBUI等等。Linux 吸引了很多人,使他们想在显示代码上做些尝试。在当前的虚拟终端中,其它的这些系统会在终端切换时引起很多问题。在切换虚拟终端后,新激活的显示系统允许完全操作硬件,包括重新初始化、重调程序和擦除 VRAM( 显存)。当你回切时,原系统应当从改变了的硬件状态中恢复过来。不仅是这些小部件会在虚拟终端切换时引起问题。你可能需要在控制台系统和 X、甚至 X 和 Xegl 这两个桌面之间做切换。选用这些替代部件,对一个使用 14 个寄存器和 32KB 的显存的 VGA 适配器来说是个好的选择。但它们对拥有 300 个寄存器、512MB显存的独立GPU协处理器,这样更好的模块来说,不是一个好办法。

有效协作

我相信此问题最好的办法是让内核,给每片显卡提供一个简单的、支持广泛的设备驱动。这就意味着像 fbdev 和 DRM 一样的冲突显卡必须合并进一个协调的系统中。这也就意味着,在内核态读取硬件驱动的同时,在用户空间卸载该硬件的做法是不被允许的。我想如果 Linux 是能给种类繁多的视频卡提供一个广泛支持的标准,那么对 Radeon 新版驱动的渴求就可能会消失。

这也不意味着诸如 Fresco 的项目不能开发他们自己的视频卡驱动。这只是说,你在运行新程序之前,需要卸载标准驱动而装载定制驱动罢了。这种装载/卸载行为与内核中的其他驱动没有区别。为同一个硬件,而在两个活动的视频设备驱动中切换,将不需要再被支持。如果一个基础驱动缺少运行所需的构件,那么向标准驱动提交补丁就好。通过在标准驱动中实现所需的扩展,所有程序可以分享他们,并且在用户空间的控制台系统中切换应用将变得简单。

如果我们保持虚拟终端的快捷键切换视频驱动的设计,我想只有快捷键切换硬件和网络驱动也实现之后,这才变得公平。

OpenGL|ES

Khronos 组织是一个新标准组织,有超过一百家成员公司。Khronos 组织的最成功的标准是OpenGL ES。OpenGL ES 定义了一套在低内存系统下运行的、十分有用的 OpenGL 子集。它同时定义了 EGL,一套独立的等价于OpenGL GLX/agl/wgl的应用程序接口的平台。“EGL 提供了一个创建绘制图层的接口,以供包括OpenGL ES 和 OpenVG 在内的客户端程序接口绘图使用;为客户端程序接口创建了图形上下文;同步来自客户端程序接口的,和本地平台绘图程序接口的绘图。这允许在 OpenGL ES 和 OpenVG 之间、高性能的快速混合2D和3D之间进行无缝绘制。

EGL 假定 OS 会提供窗口系统,但EGL与平台无关,并且区别于GLX/agl/wgl,EGL不局限于任何特定的窗口系统,所有用到本地窗口系统的地方都用屏蔽指针来处理。

Mesa 开发这已经讨论并同意扩展EGL,以便窗口系统能够更好的在 EGL 顶层实现。扩展核心提供了枚举可用屏幕、设置模式和帧缓存、裁剪屏幕和查询属性的 API。两个区域的地址需要被 EGL extension 记录,包括硬件光标( Hardware cursor ) 和色彩映射( Color mapping )。加入 EGL extension 的好处是给类似 Xegl 的窗口系统和服务提供了硬件控制。OpenGL 加上 EGL 和 Mesa 扩展,给大量的图形硬件,提供了一个十分轻巧的 API,这些硬件小到当前的手机,大到 Playstation 3、PCs,甚至是超级计算机。

扩展 EGL API 与 Linux 切合的很好。它提供了稳定的根基,来组织窗口系统和内嵌的应用。使用方便使它成为一个研发( R&D )和测试的平台。他让你专注于你的新应用或窗口系统,并且忘掉那些直接操作硬件的复杂工作。

我相信 Khronos 组织对 X.org 和 Linux 图形社区来讲,是一个重要的新机会。大部分的 Khronos 标准缺少相应的开源实现、开发系统支持和统一的测试。许多 Khronos 组织的出资人,售卖他们基于 Linux 的生产系统。如果 X.org 试图践行一下他们的许可证,那么它就会触及到 Khronos 组织的那些针对基于 Linux 和 Khronos 标准的开源参考实现、开发系统的中立、非盈利态度的言论。这个微妙的关系将会使得 Khronos 组织成员为 X.org 作出足够慷慨的贡献,就像 IBM 对待 Eclipse Foundation 一样。(简言之,Khronos 中有公司拿着基于 Linux 的代码卖钱,X.org 以此为要挟,让 Khronos Group 成员给 X.org 贡献代码)

像素级完美(Pixel Perfect)

像素级完美的精确性很难把握。你所能做的就是实现精确性的分级。错误的来源到处都是,LCD 背光均匀度、印刷油墨的浓度、DAC( 自主访问控制 )的质量、纸张对光的反射率、色彩匹配上的问题、GPU 上不同的绘图算法,等等。OpenGL 在实现上并不负责像素级完美的精确性。最能接近像素级完美的办法是在每个目标机上使用同一版本的 Mesa。顺便提一句,X server 也不是像素级完美的。我的经验是用放大镜在足够近的距离看时。分不清差别就达到像素级完美了。人们对这点感到困惑。如果你拿着OpenGL点阵图去显示,它只会复制每个像素点到屏幕,除非你告诉它,否则是不会变的。像素点绘图精确性的争论更多的在可缩放的矢量图形上,比如直线。

亚像素反锯齿字体( Subpixel antialiased fonts )不是个问题,OpenGL 提供了显示亚像素、反锯齿字体的解决方案。如果需要,OpenGL 就有显示字符(glyphs)的机制,而且这种机制被 X server 引用了。由于字符显示的机制是相同的,把绘图做成固定算法算法是我们不期望的,这会严重阻碍长远的进步。例如,这篇文章(视频)仔细研究了在GPU上生成字体的崭新方式。这儿有另一篇有趣的文章,”Resolution Independent Curve Rendering using Programmable Graphics Hardware”,Loop 和 Blinn 著,收录于SIGGRAPH 2005。如果字体拉伸后恰好达到像素完美标准,那么这些新技术就不再被需要。这些字符就可以被GPU渲染后,使用内嵌在硬件中的算法输出,同时不能被用户更改。为显示字符而把字符轮廓混合成图片,只是显示字符的其中一种方法。可编程的GPU正在提供越来越多的选择。然而长效支持的 API 都应该把这点考虑进去。

三代窗口

在当前的X server和其它窗口系统中,窗口使用画家算法在裁剪区域内绘图。画家算法模仿真实绘画——每个连续的图层都会遮蔽先前的图层。你先画背景,再在Z轴的反向绘制每个窗口。如果如果窗口不透明,并且你知道窗口所在的位置,那么你可以优化这个算法( 就像 X 那样 ),使用矩形屏幕裁剪,以便屏幕上的每个像素点只绘图一次。通过跟踪矩形被破坏的部分来提高性能。只有在破话区域内的窗口需要重绘,并且其它部分被裁剪区域保护起来。更长远的好处被称为“存在下面”( save unders )。窗口系统会标记一些绘图,比如菜单的出现,它每次出现都会重绘相同的内容。一个“存在下面”会把这段屏幕存储在弹出窗( popup windows )的最下层,并在需要时替换它。

在近期的设备中,混合 ( 注:composite,一个X扩展也叫这个词 )可以增强性能。使用混合技术,窗口可以在脱离屏幕的、不可视觉化的视频内存中绘图。窗口管理随之把这些窗口混合起来,形成可视的屏幕。混合过程仍然使用画家算法,但是因为窗口管理其拥有所有窗口的内容,所以实现半透明窗口变得可能。透明效果的实现需要混合窗口上下的内容,在它被背复制到扫描输出缓冲器( scanout buffer )时。这被称作α混合。大多数现代硬件支持它。绘图闪烁的故障也被双缓冲的应用而排除。在这种模式下,所有的东西仍是 2D ,但有一个简单的 Z 轴上的排序。

高端多纹理硬件可以完全规避画家算法。每个窗口交叉起来的感兴趣区域( ROI,region of interest ),受到可分离的纹理的约束。你会在屏幕上绘制一个多边形,并把它当作窗口。这些多边形会有合适的纹理坐标,在多边形的顶点处,给每个窗口/纹理,然后纹理合成硬件会组合所有窗口/纹理,生成期待的结果。在一个极端的情况下,整个屏幕会通过渲染一个多纹理矩形来重绘。这个技术是非常快的,你甚至可以不需要双缓冲绘图,因为它的闪烁非常弱。多纹理绘制整个桌面超越了今天的硬件条件,但是这个领域是一个随着每代硬件更新而快速增长的领域。

Xgl 实现了混合模型。虽然这不是必要要求,但是 Xgl 使用了一个新的 OpenGL 构件,帧缓冲对象( FBO,framebuffer objects )。使用这个OpenGL扩展,FBO可以允许窗口管理器有效分享离屏应用窗口。这个扩展不是想象中的难以实现,我们已经在线程之间分享了 Pbuffers。对应用来讲,FBO像一个正常的绘图窗口。对窗口管理器来说,窗口好像纹理,并且可用正常的绘图命令操作。在这个模型里,应用窗口仍然是2D,但是现在写一个基于 OpenGL的窗口管理器成为可能, 比如 Luminosity。一个基于OpenGL的窗口管理器可以使用 Z 缓冲,并且移除严格的2D-Z轴的限制。比如,一个窗口可以转换成Z轴上的波浪式结构,与其它窗口重复交错。

Looking Glass demo 最后可以移除应用上的 2D 限制,给予他们厚度或者其它 3D 图形。例如,一个弹出菜单可以在更高的3D坐标中弹出。当你用一个采光源与之结合时,下落的阴影会显示得更加自然,再也不用使用2D下的人工绘制。绘制好看的阴影是件复杂的事,但是这个工作在OpenGL下变得简单。Sun 公司的Project Looking Glass就是这类窗口管理器。窗口厚度也在Looking Glass 样例视频中有所展示。Looking Glass用未root的X server,来运行现成 X 程序。如此以来窗口,加上厚度,再加上真实3D应用,就可以应用在 CD 更换的样例上。

3D 桌面的概念并不奇怪。自从我们离开平铺式管理器,进入Windows 1.0 这类重叠窗口后,我们已经有了一些3D桌面——Z轴上的顺序被引进后,桌面就成了非透视的3D空间。3D概念贯穿整个桌面,看看菜单和弹窗。实现混合技术的最大理由是提供下落式阴影,很明显,这是3D空间的2D表现。甚至类似按钮的控制都是用2D模拟,使它看上去像3D。为什么不用3D绘制他们,让用户控制光源。窗口半透性显然引入了3D概念。为什么我们不承认桌面是3D空间,并且应该用3D图形来绘制?

划清界线

这个问题上,有些争论在。事实上 Linux 只有一种选择,把开源代码过渡到基于GPU的桌面——Mesa OpenGL上。当然我们可以克隆DirectX,但是谁来写所有的代码和驱动?OpenGL不是一个坏选择。无论我们在Linux上用什么,都要有类似这么一个东西:它被ARB( architecture review board )标准化和控制。它有良好的设计和完善的文档。他有良好的开发和使用。大学开设OpenGL相关课程,也有许多相关书籍出版。有趣的应用和游戏也跑在OpenGL上。

图形设备驱动的直线应该在什么层级上绘制?XXA和fbdev在较低层级中画线。这些API关心他们自己在帧缓冲区像素、位传送块(bitblt),或者再加多一个直线绘制。任何图形芯片关于此层级提供的芯片,或者难以理解的,或者需要芯片特定API的泄露才能理解。Xgl用许多不同的方法画直线,它用远高于OpenGL API的层级来画直线。我相信Xgl选择了更好的方式。你肯定想在高于硅芯片中的各种构件的层级来绘制直线,因为硅产业的发展就不会阻碍到API。我不相信Linux会从类似DirectX那样,有大量APIs的程序中受益。更好的模式是选取一个高层级,提供相关功能的软件实现–Mesa,然后用加速的硬件实现–DRI。我们也有多种OpenGL实现:Mesa、Nvidia、ATI。使用哪一个是你的选择,不管你信不信,一些人喜欢专利驱动。

Xgl被设计成短期的过渡方案。Xgl模式一直以来都在逐步替代现有X server绘图系统,用一种基于OpenGL设备驱动的兼容方式。Xgl保留了所有现存的X APIs,新的都没提供,旧的也没取消。Xgl是高层级的,跨平台的。他是标准代码,并且需要接入到一个特定的OpenGL环境中。一个接口,Xglx,是为GLX API准备的。另一个接口,Xegl在交叉平台EGL API上工作的。注意EGL以一种平台独立的方式替代了GLX、agl、wgl APIs。使用 Mesa 扩展,EGL可以控制低层次的帧缓存。这个组合提供了在无显卡设备上实现窗口系统(如X server)所需的所有事。但是Xgl是个短期的过渡设计,通过延迟对Xgl的要求,EXA修修补补地移除了大量Xgl依赖。

一个总是出现的争论是我们不应当使用OpenGL,因为所有老旧的设备仍在日新月异的环境中被使用。两个解决办法。首先,OpenGL是一个可扩展的API。通过软件回退(software fallbacks,退到旧版本),它可以在低端硬件上运行。OpenGL ES 也提供了接口 profiles。Profiles 是OpenGL接口,良好定义下的子集。如果需要我们可以定义一个最小化的profile,覆盖OpenGL接口的最小的可能。代码大小不是问题,100KB的专用OpenGL ES实现就够了。另一个OpenGL ES profile不要求浮点数支持。没有什么阻碍开源替代品的使用,如果我们投入资源的话。

另一个争论是原来的跑在硬件上的程序,改为软件运行。没有人会期待早期IBM机可以跑Windows Longhorn,但是同样的机器应能继续良好运行DOS。

这儿的关键问题是OpenGL比EXA更具扩展性。EXA扩展能力被高层级打破,它不能覆盖到3D之类的GPU构件和可编码性。使用OpenGL,让小到手机大到超级计算机,使用一个简单的接口成为可能。

把这些砖块堆起来

大量缩略词要一次性消化。举些简单的例子,看看这些库是怎么协作的:

App -> gtk+ -> X -> XAA -> hw

以上就是当前X server。应用与工具包会话,工具包用 xlib 接口调用 X 服务。X服务调用用当前的 XAA 驱动进行硬件绘制。X 和应用分属两个不同的进程。

App -> gtk+ -> Cairo -> X Render -> X -> XAA/EXA -> hw

工具包调用新的 Cairo 库。Cairo 依赖 X Render。如果EXA可用,X Render就能被加速。X 和应用分属两个不同的进程。

App -> qt -> Arthur -> X Render -> X -> XAA/EXA -> hw

Arthur 是 Trolltech 公司的,它和 Cairo 等价,且相差无几。

App -> gtk+ -> Cairo -> glitz -> GL -> hw

工具集使用新的Cairo库。Cairo选择glitz后端,以便OpenGL直接渲染。得益于OpenGL的直接渲染,每个工作都被加速,并且在各自的单线程中绘制。

App -> gtk+ -> Cairo -> X Render -> Xgl -> EGL(standalone) -> GL -> hw

上述状况下,工具包选择带 xlib 后端的 Cairo,然后与 X Render 下的 Xegl 对话。Xegl 使用 glitz 实现自身渲染工作。Glitz 会把渲染直接交给硬件。Xegl 和应用分属两个不同的进程。注意工具包可以选择直接使用glitz,并从单进程中返回直接渲染的结果。

App -> gtk+ -> Cairo -> X Render -> Xgl -> GLX(X) -> GL -> hw

工具包又用X Render与Xglx服务对话。Xglx不是一个独立的服务,它是一个内嵌式的服务。Xglx不是一个通常意义下的内嵌式服务,它使用嵌套输入,但是在父服务提供的单OpenGL窗口内,绘制整个桌面。如果你全屏运行,你根本不再看见父服务。那儿有三个线程,应用线程、Xglx线程、和X服务线程。得益于Xglx的直接渲染,绘图发生在应用和Xglx之间。第三个线程,X服务被卷入是因为它要提供窗口和输入。它同时掌管Xglx应用。

一个快马加鞭的未来

Linux 最后一个主要问题,是应确保一个充分利用了GPU绘制GUI的桌面。考虑到没有时间上的压力,也许一个长期的解决方案应该提上日程。我们可以设计一个新的,基于OpenGL和Cairo的服务端。

总之,可编程图形硬件的所有概念,并不在xlib和Cairo这样的API中解决。这点很重要。一个全新的主流GPU构件,其可编程性明显不能从当前的X APIs中获知。只有OpenGL才通过它的着色器编程语言(shader language)才暴露其可编程性。

当我们考虑一个新的服务端是,我们就要确保把它从概念设计分离成两个部件:平台特例部分和平台独立部分。设备驱动属平台特例部分,在提到的模块中,OpenGL作为一个设备驱动,所以他要有平台特例的实现。所有和其它Linux子系统整合的工作,都被隐藏在适当的驱动中。主要的服务端会用如套接字、OpenGL和EGL,这些跨平台的APIs来实现。一个给热插拔设备提供的跨平台API,也需要明确。服务端不需要从零开始。很大一部分代码可以从其它项目中重用,以一种全新的方式再组合。以我所见,新服务端使用的95%甚至以上的代码都有了。

模块化是写一个良好服务端的关键。设计应被分割成单独的库,库之间用标准接口通信。分离让库的替换变得容易。你也许会在不同平台上针对一个库有完全不同的实现,或者在一个平台上针对一个库有多种实现方式。当然跨平台代码是他的优势。Mesa库这样的代码可以被所有目标系统分享。

还记得DirectFB为什么要用未root的X吗?新的服务端会运行在未提权的X上,并用软件渲染的方式兼容遗留系统。DRI这种直接渲染的模式是好设计,应当在未来的服务端中保留。把X变为遗留状态,会给新服务端设计带来完全的自由。

举个例子,新服务端可以设计一个新的网络协议。近期在 Linux Journal 上,关于 No Machine 和 NX 协议的文章表明, X 协议能够被压缩至 200:1 甚至更多。新协议将基于OpenGL,减少传输上的往返延迟,提供持久的图像缓存。Chromium是一个有趣的系统,支持网络连接下多显示器的OpenGL分离显示。它们的网络库利用OpenGL的状态跟踪,作为减少网络通信的一种方式。

字型(glyphs)缓存也要分析。在当前的X服务体系中,客户端生成字型的像素图,然后发送它们到服务端缓存起来。这种模式会阻碍GPU生成字型,这点前文的引用已描述。客户端生成字体是个好想法。应用就要得负责布局(layout)。但是实际上并不要求客户端生成字型的像素图。随着GPU生成字体能力的增强,越来越多数据应传输给服务端,而非简单的像素图片。

新服务端应能弥补当前网络上声音播放和打印的不足。它应能支持合适的代理,以便重连会话。它应从设计之初就支持多用户。

事件捕获机制(event handling)是用库文件来构建子系统的好机制。Linux有evdev、内核级热插拔(kernel hotplug)、HALD-BUS。这些子系统在大多其它的平台上都没有。也因此,BSD上的输入子系统,会与Linux上的设计如此不同。我们应让主要的子系统模块变得可定制,以便充分利用运行环境。这是不同于在两种环境之间寻找最少共同点的一种解决办法。

X被全球众多和谐机构使用。当前的X服务还不足以保障敏感文件的使用安全。设计工作开展时,要时刻留意,从根基上解决安全问题。合理的资金也要长期支持,以确保正确的构件能够实现。

短期来看,大量问题仍需解决,近期的开发已着手在做。首要任务是DRM的内存管理和DRI驱动中FBO支持的重写。其次,清理掉fbdev驱动中跟硬件相关的部分,那些我们已在DRM中实现。它应支持合适的代理,以便重连会话。它应从设计之初就支持多用户。大量工作准备就绪后,新服务端的设计才会开始。

最后

我从Xegl失败的教训中学习到,构建一个显示子系统是个庞大而复杂的工作,远非一两个人就能应付的。总的来说,X.org 社区基本上没有足够的资源去建一个服务端。而分离这些资源找寻其它的路,只会导致大量流产的项目。我知道开发者愿意做自己想做,但是考虑到X.org 社区的可用资源,这种做法不可能使我们在短期内争取来一个新服务端,甚至是一个基于旧服务端的有竞争力的桌面。也许是时候让X.org出一张路线图,让我们大家都跟从了。

Jon Smirl

August 30, 2005

欢迎自由翻译和再版


猜你喜欢

转载自blog.csdn.net/cyf15238622067/article/details/78603151