Wayland 架构

本篇文章翻译自 Wayland 官方网站的 Wayland architecture。

 

Wayland 架构

理解 Wayland 架构及其与 X 的区别的绝佳途径是看一下从输入设备接收事件到屏幕发生变化的整个流程。

 

这是我们目前使用的 X 架构:

Wayland 架构_第1张图片

 

1.      内核从输入设备获得事件并通过 evdev 输入驱动发送给 X。内核会做所有的工作,驱动设备,转换不同设备的特定事件协议为 linux evdev 输入事件标准。

2.      X server 决定事件影响了哪个窗口,并且发送给监视该窗口上该事件的客户端(们)。实际上 X Server 并不知道如何做才是正确的,因为在屏幕上的窗口位置是由合成器控制的,而窗口位置可能已经进行了一系列的变换,而 X Server 是无法知道这些变换的(如按比例缩小、旋转、晃动等)。

3.      客户端查看事件并决定做什么。经常界面必须做出改变以响应事件­——也许是点击一个check box ,或者是鼠标移动到一个 button 上,button 必须被高亮显示。这样客户端就会发送一个渲染请求到 X server。

4.      当 X server 接收到渲染请求,它会把请求发给驱动从而操控硬件进行渲染。X server 也会计算渲染的绑定区域(bounding region),并把绑定区域作为damage event发送至合成器。

5.      Damage 事件告诉合成器窗口中哪一部分发生了变化,而合成器必须重新合成该窗口在屏幕上的可视部分。合成器基于它的scenegraph(合成器绘制的内容)以及 X 窗口内容负责渲染整个屏幕。然而,它必须通过 X Server 来渲染。

6.      X server 接收到合成器的请求后,会复制合成器的 back buffer 到 front buffer,或者执行 pageflip 操作。在一般情况下,X server 必须要做这一步,这样它才可以处理可能需要剪切(clipping)重叠窗口,以及判断是否可以 page flip。然而,对合成器来说,它总是处于全屏,这是另外一个不必要的上下文切换(context switch)。

 

综上所述,这种方法存在一些问题。X server既没有信息去决定哪个窗口接收事件,也不能变换屏幕坐标到窗口本地坐标。另外,即使 X 已经把屏幕上最终内容的绘制交给合成管理器去做,X 仍然控制着 front buffer 和模式设置(modesetting)。大多数 X server 的复杂性被用于处理在内核或自包含库(KMS, evdev, mesa, fontconfig, freetype, cairo, Qt, 等)中已有的功能。

 

在 Wayland 中,合成器即是显示服务器(display server)。我们把 KMS 与 evdev 的控制转交给合成器。Wayland 协议让合成器直接把事件发送给客户端,客户端直接把 damage 事件发送给合成器:

Wayland 架构_第2张图片

 

1.      内核获得事件并发送给合成器。这一步类似于 X 下的情形,而且这是一件好事,因为我们可以重用所有的内核中的输入驱动。

2.      合成器检查它的 scenegraph 以决定哪个窗口应该接收事件。Scenegraph 指屏幕上的内容,并且合成器能够理解可能被应用在 scenegraph 中的元素上的各种变换。这样,合成器可以挑选正确的窗口并变换屏幕坐标到窗口本地坐标(通过应用反向变换)。可以应用到窗口的变换种类仅限于合成器的能力,只要对于输入事件能够计算反向变换即可。

3.      和在 X 下的情形一样,当客户端接收到事件,客户端会更新界面以作出反应。但是在 Wayland 的情形下,渲染发生在客户端,并且客户端会发送一个请求到合成器,指出哪一块区域被更新了。

4.      合成器收集来自客户端(们)的 damage 请求后重新合成屏幕。接着合成器就可以通过 KMS 发送一个 ioctl 来调度 pageflip。

 

Wayland 渲染

在上述概述中,我没有说明的一个细节是:客户端实际上是怎样在 Wayland 下渲染的。在图中删除 X server 后我们同样删除了 X 客户端渲染的传统机制。但是在X下还有一种我们正使用的机制(通过 DRI2):direct rendering(直接渲染)。通过直接渲染,客户端和服务器共享一块显存(video memory buffer)。客户端链接到某个渲染库,如 OpenGL(知道如何操纵硬件和直接渲染到 buffer)。合成器接着就使用该显存作为一个 texture 来合成桌面。经过初始化设置后,客户端只要告诉合成器使用的是哪一块显存,以及何时何地渲染新的内容到显存中。

 

这就使应用程序有两种方式去更新它窗口的内容:

1.      渲染新的内容到新的 buffer,并告诉合成器使用新的 buffer 而不是老的 buffer。应用程序可以在需要更新窗口内容时每次分配一个新的buffer ,也可以预先保留2个或多个buffers ,然后在它们之间循环使用。Buffer 的管理完全在应用程序的控制下。

2.      渲染新的内容到之前已经告诉合成器使用的 buffer 中。当直接渲染到与合成器共享的 buffer 中时,这可能会导致与合成器的竞争。可能发生的情况是重画窗口内容可能会被合成器重画桌面的动作打断。如果应用程序在清除窗口之后,渲染内容之前被打断,合成器就会绘制一个空的 buffer。结果是该应用程序窗口会在空白窗口或部分渲染窗口之间闪烁。避免这个问题的传统方法是渲染新的内容到 back buffer,然后复制到合成器surface。Back buffer 可以随时分配,并且只要足够大以容纳新的内容,或者应用程序保留一份 buffer。再次,这个完全在应用程序的控制下。

 

在上述两种方式的任一种中,应用程序都必须告诉合成器 surface 哪一块区域保有新的内容。当应用程序直接渲染到共享 buffer 时,需要通知合成器有新的内容。而且,当交换 buffer时,合成器不会假设有任何东西改变了,接收到来自应用程序的请求后才会重画桌面。这样的话,即使一个应用程序传递了一个新的 buffer 到compositor,仅有该 buffer 的一小部分是不同的,如闪烁的光标或等待图标(spinner)。

 

Wayland 硬件需求

一般来讲,硬件需求包括 modesetting/display 和 EGL/GLES2。此外,Wayland 需要一种在进程间高效共享 buffer 的方式。这有两个方面:客户端和服务器端。

 

在客户端我们已经定义了 Wayland EGL 平台。在 EGL 模式下,该平台包含本地类型(EGLNativeDisplayType, EGLNativeWidnowType, EGLNativePixmapType)和创建这些类型的方法。也就是说,它是绑定 EGL 栈和 buffers 共享机制到通用 Wayland API 的融合代码。EGL 栈用于提供 Wayland EGL 平台的一种实现。完整的API 定义在 wayland-egl.h 头文件。在 mesa EGL 栈中的开源实现在 wayland-egl.c 和 plagform-wayland.c 中。

在底层,EGL 栈需要定义一个特定厂商的协议扩展,使客户端 EGL 栈能够与合成器交换Buffer 信息以共享 buffers。Wayland-egl.h 的关键是抽象上述内容,从而使客户端只需要为Wayland surface创建一个 EGLSurface,然后开始渲染即可。在开源栈中使用的 drm Wayland extension,使客户端发现要使用的 drm 设备并验证,然后就可以与合成器共享drm buffers(GEM)。

 

Wayland 的服务器端是合成器与核心UX(通常集成了任务切换、应用程序 Launcher、Lock screen等)结合为一体的应用程序。服务器运行在模式设置 API 之上(kernel modesetting, OpenWF Display 或类似物),使用 EGL/GLES2 合成器和硬件Overlay(如果可用)来合成最终的UI。支持模式设置、EGL/GLES2和 overlays 应当是目前标准硬件制造的部分。启用Wayland 的另一个需求是 EGL_WL_bind_wayland_display 扩展,该扩展使合成器能够从一个通用的Wayland 共享 buffer 创建一个 EGLImage。这类似于从X pixmap 创建 EGLImage 的EGL_KHR_image_pixmap 扩展。

 

该扩展有一个设置步骤,就是你必须绑定 EGL display 到 Wayland display。然后,当合成器接收到来自客户端(通常当客户端调用eglSwapBuffers)的通用Wayland buffers时,它就可以把结构 wl_buffer 指针作为 EGLClientBuffer类型的参数,并以EGL_WAYLAND_BUFFER_WL作为target,传递到 eglCreateImageHKR函数。这样就会创建一个EGLImage,这个EGLImage 可以作为一个texture 被合成器使用,或传递到模式设置代码作为一个 Overlay plane。再次,这也由特定厂商的协议扩展实现,也就是在服务器端会接收驱动的关于共享buffer特定细节,然后当用户调用 eglCreateImageKHR时把 buffer 转换成一个EGL 图形。

 

https://blog.csdn.net/basilc/article/details/8074837

你可能感兴趣的:(Wayland 架构)