Weston Misc
1. 启动Weston
1.1 实现内容
- 解析cmdline
- 初始化log系统
- 创建wl_display对象,并侦听client接入
- 创建weston_compositor对象,从而创建global resource compositor 和 shm, 以前其他资源
- load backend, 默认为drm_backend, 在drm_backend初始化的过程中会load gl_renderer
- load shell, 默认为desktop-shell.so
- 调用wl_display_run( ) 循环等待event的发生
1.2 伪代码
int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data){
// 初始化 layoutput_list
wl_list_init(&wet.layoutput_list);
...
// parse command line
...
// init log system
...
// 调用wayland提供的函数wl_display_create()
// 创建 wl_display 对象
display = wl_display_create();
...
// 创建 weston_compositor 对象
// --> 创建 global resource compositor and shm 以及其他resource
weston_compositor_create( );
// 读取config for compositor
...
// load backend,通用的为load_drm_backend( )
// 最终调用 对应backend实现的weston_backend_init( )
// 在backend 初始化的过程中会装载 gl_renderer
load_backend(compositor, backend)
...
// 创建socket, 侦听client的连接请求
weston_create_listening_socket(display, socket_name)
...
// load shell, 默认为 desktop-shell.so
wet_load_shell(compositor, shell, ...)
...
// loop, 循环等待event的发生
wl_display_run(display)
...
// 退出流程,资源的释放
}
1.3 backend, renderer, shell的作用
结构图
backend
{
destroy()
repaint_begin() //composite之前调用
repaint_cancel() // 中途取消
repaint_flush() // composite 完成后调用, 可用于实现提交到display
create_output() // 创建weston_output
device_changed()
can_scanout_dmabuf()
}Weston_backend, Compositor->backend
renderer: renderer接口供backend内部使用,外部通过调用backend接口触发
{
display_create()
output_window_create()
output_pbuffer_create()
output_destroy()
output_set_border()
create_fence_fd()
} gl_renderer_interface
2. Client 动作
2.1 Client的接入和global资源代理的创建
流程
- 连接display
- 获得registry,注册listener,用于处理weston资源变化时的callback
- 根据资源变化的callback, 创建各类资源的proxy
- 进入loop,不断调用wl_display_dispatch( ),使得wayland内部循环处理各类event
伪代码
static void global_resource_found(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
// 通过字符串interface 判断是什么resource,
// 通过wl_registry_bind() 创建对应的 resource proxy
if (strcmp(interface, "wl_compositor") == 0) {
// 构建了 compositor 的 proxy
compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4));
}else if (strcmp(interface, "wl_shm") == 0) {
// 构建了 shm 的proxy
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
}
...
}
// 当weston的global resoure发生变化时,通过如下回调函数通知到client
wl_registry_listener registry_listener = {
// 发现新global resource的回调函数
global_resource_found,
// global resource remove的回调函数
global_resource_remove
}
int main(int argc, char** argv) {
// 1. 调用wayland提供的 wl_display_connect( ), 连接到weston( wayland server)
// 对应到weston启动中的weston_create_listening_socket()
display = wl_display_connect(NULL);
...
// 2. 获取 wl_registry, 并侦听它的callback
registry= wl_display_get_registry(display);
wl_registry_add_listener(registry, ®istry_listener, display);
...
// 3. 循环等待,
// 调用wl_display_dispatch( ),由wayland处理weston发来的event
while(ret != -1){
ret = wl_display_dispatch( );
}
}
2.2 内部weston - client 通讯机制
request: Client –> Server
event : Server –> Client
术语上,Wayland 中把 Client 发给 Server 的跨进程函数调用称为 request,反方向的跨进程函数调用称为 event。 本质上,它们处理的方式是类似的。
要让两个进程通过 socket 进行函数调用,首先需要将调用抽象成数据流的形式。这个数据流应该包含函数名、参数等信息。
IPC 函数的接口定义是应该同时包含在 Client 和 Server 端的库中的,其中包含了接口对象所支持的 request 和 event 的函数签名。因此这部分不用传输,只要传输目标对象 id,方法 id 和参数列表这些信息就可以了。
这些信息会通过 wl_closure_marshal()写入 wl_closure 结构,再由 serialize_closure()变成数据流。
等到了目标进程后, 会从数据流通过 wl_connection_demarshal()转回 wl_closure。
IPC 图示
object IPC 机制
这个过程类似于 Android 中的 Parcel 机制。那么 问题来了,参数中的整形,字符串什么的都好搞,拷贝就行。但如果参数中包含对象,我们不能把整个对象 拷贝过去,也不能传引用过去。那么需要一种机制来作同一对象在 Server 和 Client 端的映射,这是通过 wl_map 实现的。
wl_map 在 Client 和 Server 端各有一个,它们分别存了 wl_proxy 和 wl_resource 的数组,且是 一一对应的。这些对象在这个数组中的索引作为它们的 id。这样,参数中的对象只要传 id,这个 id 被传到目 的地后会通过查找这个 wl_map 表来得到本地相应的对象。在功能上类似于 Android 中的 BpXXX 和 BnXXX。
wl_proxy 和 wl_resource 都包含 wl_object 对象。这个 wl_object 和面向对象语言里的对象概念类似,它有 interface 成员描述了这个对象所实现的接口,implementation 是这些接口的实现函数的函数指针数组,id 就是 在 wl_map 结构里数组中的索引。
前面所说的 Client 绑定 Server 端资源的过程就是在 Client 端创建 wl_proxy, 在 Server 端创建 wl_resource。然后 Client 就可以通过 wl_proxy 调用 Server 端对应 wl_resource 的 request, Server 端就可以通过 wl_resource 调用 Client 端对应 wl_proxy 的 event。
这个映射过程如下图所示(以 wl_registry 为例)
2.3 Client 创建各类资源proxy
// wl_surface
wl_surface = wl_compositor_create_surface(compositor)
// wl_buffer
wl_shm_pool = wl_shm_create_pool( )
wl_buffer = wl_shm_pool_create_buffer( )
// attach buffer to surface
wl_surface_attach(wl_surface, wl_buffer)
/* 以下与窗口的管理\显示相关 */
// xdg_surface
// xdg_wm_base 它也是一个global resouce,对应到 desktop-shell
xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, wl_surface)
// xdg_toplevel
xdg_toplevel = xdg_surface_get_toplevel(xdg_toplevel)
// wl_keyboard
// wl_seat 是一个global resource
// 通过wl_keyboard 创建一个listener就可接收按键
wl_keyboard = wl_seat_get_keyboard(wl_seat)
wl_keyboard_add_listener(wl_keyboard, keyboard_listener)
// wl_pointer 鼠标指针
// 通过wl_pointer 创建一个listener可以接收鼠标的移动信息
wl_pointre = wl_seat_get_pointer(wl_weat)
wl_pointer_add_listener(wl_pointer, pointer_listener)
... ...
2.4 Client 渲染
伪代码 simple-egl.c
/*-------------------- egl 初始化工作 ------------------------*/
// egl lib 应该要支持wayland。
// 这样在调用一些egl接口时,在其内部会调用wayland接口与Wayland server交换信息
// 如函数:eglGetDisplay( ) , eglCreateWindowSurface( ), eglSwapBuffers 等
// 1. 获取egl_display
egl_display = weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR, wl_display, ...)
or
egl_display = eglGetDisplay(wl_display)
// 2. 初始化 egl
eglInitialize(egl_display)
// 3. 通用elg 配置
eglGetConfigs()
eglChooseConfig( )
eglCreateContext( )
/* ----------------------gl 准备工作------------------------------*/
// 1.创建 shader
glCreateShader( )
// 2.创建 Program
glCreateProgram( )
// 3. attach shader to program
glAttachShader( )
// 4. Link program
glLinkProgram( )
// 5. 使用program
glUseProgram( )
/*-------------------------wl_surface 关联egl_surface----------------*/
wl_egl_window->surface = wl_surface;
wl_egl_window->width = width;
wl_egl_window->height = height;
eglCreateWindowSurface(egl_display, ... , wl_egl_window)
/*------------------------------------------------------------------*/
gl 绘制图形
/*-------------------------------------------------------------------*/
// 内部实现应该调用wayland接口来swap buffer
eglSwapBuffers(egl_display, egl_surface)
...
2.5. Client 提交渲染好的surface
wl_surface_commit( )
3. 各个Surface的合成与呈现呈现·
3.1 流程
- compositor遍历每个weston_output 发起repaint。weston_output_schedule_repaint( )
- 通知weston_output具体实现–backend_output, 开始repaint的前期准备工作, 对应函数start_repaint_loop( ), drm实现暂无内容
- backend_output通知compositor可以开始output repaint
- compositor 调用weston_output相关backend的repaint_begin( ),drm_backend 创建了pending_state
- compoistor 调用weston_output_repaint(), 开始repaint。 调用weston_compositor_build_view_list( ) 构建view_list, 得到output的一个paint_node_z_order_list
- 调用drm_backend assign_planes( ) 设置输出plane
- 调用drm_backend drm_output_repaint( ), 最终指向gl_renderer_repaint_output( )
- 依据paint_node_z_order_list, OpenGL依次建立shader,texture等进行渲染
- 全部完成后,提交呈现
sequenceDiagram
participant C as Compositor
participant O as Weston_output
participant B as Backend
loop 遍历weston_output_list
C ->> O: 要开始repaint_loop <br />call backend_output start_repaint_loop( )
C ->> B: repaint_begin( )
Note right of B: 创建 pending_state
C ->> O: 通知output repaint
O ->> C: 构建view_list, build_view_list( )
Note left of C:构建paint_node_z_order_list
C ->> B: assign_planes( ),设置输出plane
C ->> B: drm_output_repaint( )
Note right of B: 调用OpenGL API <br/>结合paint_node_z_order_list<br/>进行渲染
end