WayLand的架构和协议
1. Wayland简介
1.1 Wayland
是啥?为啥它这么重要?
嘿,你知道吗?有时候咱们用电脑的时候,是不是觉得图形界面有点慢、有点卡?那是因为我们还在用一个叫X Window System (X11)
的老伙计。这个老伙计虽然功不可没,但毕竟年纪大了,有些力不从心啦。这时候就轮到我们的新朋友——Wayland
登场了!
1.2 Wayland
背后的故事:它是怎么来的?
话说2008年的时候,有个叫Kristian Høgsberg的人,他想:“为什么不能有一个更好的方式来处理图形呢?”于是他就开始了Wayland
项目。经过多年的努力,Wayland
逐渐成长起来,现在已经被很多主流桌面环境如GNOME
和KDE Plasma
所采用。可以说,Wayland
已经成为了Linux社区的新宠儿。
1.3 Wayland
的梦想:它想带给我们什么?
简单来说,Wayland
想让你的电脑图形体验变得更棒!它通过简化图形渲染流程,直接与硬件对话,减少不必要的通信层,从而提高了性能和安全性。对于开发者而言,这意味着更少的代码维护工作;对于用户来说,则意味着更流畅的操作体验。
2. Wayland架构
2.1 X和Wayland:两者架构有何不同?
来聊聊X
是怎么处理输入到显示的
想象一下,当你在使用基于X
的应用时,每次你点击鼠标或者敲击键盘,都会触发一系列复杂的步骤,直到最终的变化出现在屏幕上。这个过程有点像一个接力赛,每个环节都至关重要。
首先,内核会通过evdev
输入驱动捕捉来自设备(如鼠标或键盘)的事件,并将这些事件发送给X Server
。这里,内核做了很多重活,它负责管理硬件并将各种特定于设备的事件协议转换成统一的Linux evdev
标准格式。
接下来,X Server
要决定哪个窗口应该接收这个事件,并将其转发给那些已经注册了该类型事件监听的客户端。但是,这里有个小问题——X Server
并不完全清楚窗口的实际位置和状态,因为这些是由合成器控制的,而合成器可能会对窗口进行各种变换(缩放、旋转、特效等),这些变化是X Server
所不了解的。
然后,接收到事件的客户端根据需要更新用户界面。比如,你可能点击了一个复选框,或者鼠标指针进入了某个按钮区域,这时就需要高亮显示。因此,客户端会向X Server
发送渲染请求。
当X Server
收到渲染请求后,它会把任务交给显卡驱动,让其直接编程硬件完成渲染工作。同时,X Server
还会计算出渲染影响的屏幕区域,并向合成器发送一个损坏事件(damage event),告知合成器需要重新组合那一部分屏幕内容。
最后,合成器会通知X Server
,由它来决定是直接复制合成器的后台缓冲区到前台缓冲区,还是执行一次页面翻转(pageflip)。这一步骤对于处理窗口重叠等情况非常重要,但对于总是全屏显示的合成器来说,就显得多余了,因为它引入了不必要的上下文切换。
现在轮到Wayland
登场啦!
在Wayland
的世界里,整个流程变得更加简洁高效。这里的关键点在于,合成器就是显示服务器本身,这意味着我们可以直接从内核获取事件并立即传递给合成器,而不需要经过额外的中间层。我们还把KMS
(模式设置)和evdev
的控制权交给了合成器,进一步简化了架构。
这个过程大致如下:
-
内核依旧负责捕捉输入事件并通过
evdev
接口发送给合成器。这部分与X
的情况相似,所以我们可以继续利用内核中已有的输入驱动。 -
合成器则会根据它的场景图(scenegraph)来确定哪个窗口应该接收事件。由于合成器掌控着所有窗口的状态及其可能应用的各种变换,它可以准确地选择正确的窗口,并将屏幕坐标转换为窗口本地坐标。只要合成器能够计算出针对输入事件的逆变换,那么理论上可以对窗口施加任何类型的变换。
-
客户端接收到事件后,就像之前一样更新用户界面。不过,在
Wayland
下,渲染是在客户端这边完成的,之后只需告诉合成器哪些区域被更新了即可。 -
合成器收集来自各个客户端的损坏请求,然后重新组合屏幕内容。这个时候,合成器可以直接调用
ioctl
命令通过KMS
安排页面翻转,无需再经过X Server
这一环。
这样做的好处是减少了不必要的通信开销,提高了系统的响应速度和效率。Wayland
的设计使得每个组件都能专注于自己最擅长的事情,从而构建出了一个更加流畅且安全的图形环境。
2.2 Wayland
的图像渲染
在Wayland
的世界里,所有的视觉内容都是通过缓冲区
来管理的。应用程序想要显示任何东西时,它会在自己的内存空间中创建一个或多个图像缓冲区,并将这些缓冲区提交给合成器(compositor)
。合成器
扮演着艺术家的角色,负责将这些图像组合成最终的画面,展示给用户。
Wayland
利用现代GPU
的强大功能,确保无论是播放视频还是运行3D游戏,都能提供即时响应的速度。所有绘图命令都通过DMA-BUF接口发送给GPU,这保证了即使在多任务环境中也能保持高效运作。
客户端渲染机制
当X Server
被移除后,传统的X
客户端渲染机制也随之消失。但Wayland
引入了直接渲染(direct rendering
)作为替代方案,这一机制已经在X
的DRI2
中有所体现。在直接渲染模式下,客户端
和合成器
共享一块视频内存缓冲区。客户端链接到如OpenGL
这样的渲染库,该库知道如何编程硬件,并能够直接将内容渲染到共享缓冲区
中。合成器
则可以将这块缓冲区用作texture
,当它合成桌面时就能显示出更新的内容。
硬件支持的重要性
为了使Wayland
能有效工作,良好的硬件支持至关重要。这包括但不限于模式设置/显示管理和EGL/GLES2等功能。此外,Wayland
还需要一种机制来高效地在进程间共享缓冲区,这部分涉及到客户端和服务器端两个方面的工作。
-
客户端实现:定义了一个
Wayland EGL
平台,它包含了一套原生类型(如EGLNativeDisplayType
、EGLNativeWindowType
和EGLNativePixmapType
),以及创建这些类型的接口。这是将EGL栈及其缓冲区共享机制绑定到通用Wayland API
的胶水代码。开源实现中,mesa EGL栈中的wayland-egl.c
和platform_wayland.c
文件实现了这一功能。 -
服务器端实现:
Wayland
的服务器端即为合成器,它通常集成了任务切换器、应用启动器、锁屏界面等核心用户体验组件。服务器运行在一个模式设置API之上(如内核模式设置、OpenWF Display或其他类似技术),并通过EGL/GLES2合成器和硬件叠加层(如果有的话)来组合最终用户界面。为了让合成器能够处理来自客户端的Wayland共享缓冲区,必须启用EGL_WL_bind_wayland_display
扩展,这样合成器就可以从任意Wayland共享缓冲区创建一个EGLImage对象。这个过程首先需要绑定EGL显示到Wayland显示,之后每当合成器接收到客户端发来的缓冲区(通常是通过调用eglSwapBuffers
时),它可以将wl_buffer
指针传递给eglCreateImageKHR
函数,以EGLClientBuffer
参数的形式创建一个EGL图像,用于后续作为纹理或通过模式设置代码作为叠加平面使用。
3. Wayland协议
WayLand
协议, 是WayLand客户端与WayLand合成器之间的通讯协议
官方提供的WayLand
协议的参考实现
https://gitlab.freedesktop.org/wayland/wayland
官方提供的WayLand合成器的参考实现
https://gitlab.freedesktop.org/wayland/weston
3.1 协议传输与消息格式
Wayland
协议通过UNIX
域流套接字进行传输,通常情况下,该套接字的端点名为wayland-0
(尽管可以通过环境变量WAYLAND_DISPLAY
来更改)。从Wayland
1.15版本开始,实现可以选配支持位于文件系统任意位置的服务器套接字端点,只需将WAYLAND_DISPLAY
设置为服务器端点监听的绝对路径即可。
每条消息被构造成由多个32位字组成的序列;值以主机的字节序表示。消息头部包含两个32位字:
- 第一个字是发送者的对象ID(32位)。
- 第二个字分为两部分:高16位是消息大小(以字节为单位),从头部开始计算(即最小值为8字节);低16位是请求或事件的操作码。
有效载荷描述了请求或事件的参数。每个参数总是对齐到32位边界;如果需要填充,填充字节的值未定义。没有前缀描述类型,而是隐式地从XML规范推断出来。
以下是参数类型的表示方式:
-
int
,uint
- 表示32位有符号或无符号整数值。
-
fixed
- 有符号的24.8定点数。它是一种提供符号位、23位整数精度和8位小数精度的有符号十进制类型。在C API中,它作为一个不透明的结构体,并提供了转换为双精度浮点数和整数的帮助函数。
-
string
- 以一个无符号的32位长度(包括空终止符)开头,接着是字符串内容,包含终止的空字节,然后是对齐到32位边界的填充。空值用长度为0表示。
-
object
- 32位对象ID。空值用ID为0表示。
-
new_id
- 32位对象ID。一般情况下,新对象使用的接口可以从XML推断出来,但在接口未指定的情况下,
new_id
之前会有一个指定接口名称的字符串,以及一个指定版本的无符号整数。
- 32位对象ID。一般情况下,新对象使用的接口可以从XML推断出来,但在接口未指定的情况下,
-
array
- 以32位数组大小(以字节为单位)开头,接着是数组内容的直接拷贝,最后是对齐到32位边界的填充。
-
fd
- 文件描述符并不存储在消息缓冲区中,而是在UNIX域套接字消息的辅助数据(msg_control)中。
Wayland
协议并未明确规定辅助数据在流中的确切位置,只规定文件描述符的顺序与消息及其内部的fd
参数在网络上的顺序相同。
这意味着流中的任何字节,甚至是消息头部,都可能携带带有文件描述符的辅助数据。
客户端和合成器(compositor
)应当排队等待接收的数据,直到他们有完整的消息可供处理,因为文件描述符可能会早于或晚于对应的数据字节到达。
3.2 核心接口概述
Wayland
协议设计了一套核心接口,用于客户端与服务器之间的交互。这些接口提供了请求、事件和错误(实际上也是特殊类型的事件)的功能,如前文所述。虽然具体的合成器(compositor
)实现可能会提供作为扩展的自有接口,但有一些接口是预期一定会存在的。
以下是Wayland
的核心接口:
-
wl_display
- 作为核心全局对象,它充当了客户端与
Wayland
服务器之间通信的起点。通过这个接口,客户端可以获取其他全局对象,并发送或接收消息。
- 作为核心全局对象,它充当了客户端与
-
wl_registry
- 全局注册表对象,用来枚举当前可用的全局对象。客户端使用此接口来发现并绑定到它们需要的服务。
-
wl_callback
- 回调对象通常用于异步操作的完成通知,比如帧同步。
-
wl_compositor
- 合成器(
compositor
)单例,提供创建surface
和其他图形资源的方法。
- 合成器(
-
wl_shm_pool
和wl_shm
- 这两个接口共同支持共享内存池的创建和管理,使得图像数据可以直接在客户端和合成器(
compositor
)之间交换。
- 这两个接口共同支持共享内存池的创建和管理,使得图像数据可以直接在客户端和合成器(
-
wl_buffer
- 表示一个
surface
的内容,可以由多种不同方式创建,包括但不限于共享内存。
- 表示一个
-
wl_data_offer
,wl_data_source
,wl_data_device
和wl_data_device_manager
- 这组接口支持数据传输功能,例如剪贴板和拖放操作。
-
wl_shell
和wl_shell_surface
- 提供了创建桌面风格
surface
的方法,并为这些surface
提供了额外的元数据接口。
- 提供了创建桌面风格
-
wl_surface
- 屏幕上的
surface
,是所有可视内容的基础元素。
- 屏幕上的
-
wl_seat
- 输入设备的集合,每个
seat
代表一组输入设备,比如键盘、鼠标和触控屏。
- 输入设备的集合,每个
-
wl_pointer
,wl_keyboard
,wl_touch
- 分别对应指针、键盘和触摸屏等输入设备的具体接口。
-
wl_output
- 描述合成器(
compositor
)输出区域的信息,比如显示器的几何属性和模式。
- 描述合成器(
-
wl_region
- 定义了一个不规则形状的区域,可以应用于
surface
以控制其可见性或其他特性。
- 定义了一个不规则形状的区域,可以应用于
-
wl_subcompositor
和wl_subsurface
- 支持子
surface
组合,允许将一个surface
嵌入到另一个surface
中,形成复杂的用户界面布局。
- 支持子
上述接口构成了Wayland
协议的基本构建块,确保了客户端应用程序能够与合成器(compositor
)进行高效且安全的通信。
4. Wayland对X11应用的支持
为了让那些还没迁移到Wayland
上的老应用继续工作,Wayland
引入了一个叫做XWayland
的小帮手。它实际上就是一个小型的X Server
,可以在Wayland
环境中运行,接受来自传统X11
客户端的连接请求,并将它们适配到新的显示协议之上。这样一来,用户就可以在同一桌面上同时运行两种类型的应用程序,无缝切换毫无压力。
尽管Wayland
本身并不需要传统的X窗口管理器
,但在某些情况下(例如通过XWayland
运行X11
应用时),它们仍然扮演着重要角色。此时,Wayland
合成器会充当“超级管理员”,负责协调不同类型的窗口行为,确保整个系统稳定运行。
4.1 X11 应用程序的连接方式
X11
应用程序连接到Xwayland
的方式就如同它连接到任何其他X
服务器一样。Xwayland
负责处理所有的X11
请求,并且在另一端,它作为一个Wayland
客户端连接到了Wayland
合成器(compositor
)。
4.2 X Window Manager
(XWM) 和 Wayland Window Manager
(WWM)
XWM
是Wayland
合成器(compositor
)不可或缺的一部分。它使用标准的X11
窗口管理协议来管理所有通过Xwayland
运行的X11
窗口。重要的是,XWM
充当了Xwayland
窗口状态和Wayland
合成器(compositor
)的WWM
之间的桥梁。这样,WWM
就可以统一管理所有类型的窗口,无论是原生的Wayland
窗口还是X11
(即Xwayland
)窗口。这对于提供一致的用户体验至关重要。
4.3 Xwayland
的工作原理
由于Xwayland
依赖Wayland
进行输入和输出操作,所以它不需要像Xorg
那样使用设备驱动程序。这意味着不会使用任何xf86-video-*
或xf86-input-*
模块。此外,Xwayland
服务器没有配置文件。对于可选的硬件加速渲染,Xwayland
采用了GLAMOR
库。
4.4 单一实例的Xwayland
通常,一个Wayland
合成器(compositor
)只会产生一个Xwayland
实例。这是因为许多X11
应用程序假设它们可以通过X
服务器与其他X11
应用程序通信,而这需要共享同一个X
服务器实例。这也意味着,除非Wayland
合成器(compositor
)特别选择通过为特定应用程序生成独立的Xwayland
实例来打破这种通信,否则Xwayland
不会保护或隔离各个X11
客户端。值得注意的是,X11
客户端与Wayland
客户端之间是天然隔离的。
4.5 Xwayland
的兼容性挑战
尽管Xwayland
尽可能地兼容传统的X
服务器,但要达到100%的兼容几乎是不可能的。特别是桌面环境组件中的X11
窗口管理器基本上不受支持。一个X11
窗口管理器无法识别原生的Wayland
窗口,因此它只能管理X11
窗口。然而,必须有一个XWM
来保留独占的窗口管理角色,以便Wayland
合成器(compositor
)可以适当地显示X11
窗口。对于其他的桌面环境组件,如分页器和面板,为了在WWM
中通过XWM
支持它们而添加必要的接口往往被认为是不值得的。
4.6 窗口标识与同步通信
在Xwayland
中,一个X11
窗口可能会对应于Wayland
中的一个wl_surface
对象。这个wl_surface
对象用于输入和输出:它被输入事件引用,并用于向Wayland
合成器(compositor
)提供X11
窗口的内容。X11
窗口和wl_surface
存在于不同的协议流中,因此需要将它们匹配起来以使XWM
能够正常工作。
当Xwayland
在Wayland
上创建一个wl_surface
时,它也会发送一个类型为原子"WL_SURFACE_ID
"的X11 ClientMessage
给对应的X11
窗口,该消息的第一个32位数据元素携带的是wl_surface
的Wayland
对象ID。这是XWM
关联wl_surface
与X11
窗口的方法。需要注意的是,创建wl_surface
的请求和ID消息可能以任意顺序到达Wayland
合成器(compositor
)。因此,在实现XWM
时,需要格外小心以避免(随机)死锁。强烈建议所有来自XWM
的X11
通信都应该是异步的,因为证明同步或阻塞的X11
调用不会导致死锁通常是极其困难的。所有Wayland
通信已经天生就是异步设计的。
5. 总结
本文介绍了Wayland
作为新一代显示服务器协议,旨在替代传统的X Window System (X11)
,为Linux
及其他类Unix
系统提供更高效、安全的图形界面体验。
Wayland通过简化图形渲染流程,直接与硬件对话,减少了不必要的通信层,提高了性能。其架构中合成器即为显示服务器,可直接从内核获取事件并传递给合成器,避免了额外中间层,从而减少通信开销。Wayland
利用现代GPU功能确保图像内容即时响应,通过DMA-BUF接口发送绘图命令保持高效运作。
在Wayland协议方面,它定义了一套核心接口,用于客户端与服务器之间的交互,包括wl_display、wl_registry等,确保应用程序能与合成器进行高效安全通信。
此外,为了兼容传统X11
应用,引入了XWayland
作为小型X Server,在Wayland环境中运行,使新旧应用无缝共存。然而,尽管XWayland尽力兼容,仍存在一定的兼容性挑战,特别是对于X11窗口管理器的支持有限。