Waylandx学习记录
写这篇文章的主要目的是为了记录一下自己的个人理解,便于以后回忆起来
一手资料肯定是推荐:Wayland官网
我们知道Wayland的初衷是为了颠覆X,而提出的一种架构
我们先大致了解一下X的架构(Linux下面主要的代表是Xorg)
介绍一个点击事件场景在X架构下是怎样的
- 首先内核收到硬件的点击事件,发送一个udev事件给到X server
- X server决定哪个客户端会被影响(这个客户端需要注册对应的事件),然后发送给这个客户端
- 客户端收到这个事件,通常一些UI应用会根据这个事件做出对应的处理,例如鼠标点击后会高亮,客户端如果想实现这个效果,就需要发送一个请求给到X Server
- X Server收到这个请求,就会把这个请求发送给driver,让它去渲染
- driver处理完后,就会发送一个damage信息给到compositor,compositor就知道哪个window需要change,compositor负责整个屏幕的渲染,把每个window的内容,根据图层,合成一张图片
- X Server从compositor收到这个渲染请求,讲这部分内容拷贝到CRTC扫描的帧存地址,或者如果开启了page flip,就会切换前后缓存区
Wayland架构下
通过两个架构图,你可以明显看到Wayland的架构更简洁明了
1、内核收到输入设备的事件,内核将这个事件发送给compositor
2、compositor会决定发送给哪个compositor客户端
3、客户端收到这个事件,就直接处理这个事件了,包括渲染,然后将damage区域通知给到compositor
4、compositor收到damage信息,然后更新整个画面,compositor能直接通过DRM跟内核交互,实现page flip
从上面两种架构的对比,我们可以看到明显的性能差别
在X的架构下,client、compositor、X Server都是属于单独的进程,跨进程频繁的交互,必然会导致性能下降
在Wayland的架构下,compositor和X Server合并成为一个compositor,跨进程的交互减少,必然导致性能的提升
Wayland架构下,weston是compositor的一个实现,类似于Xorg是X Server的一个实现
下面我们介绍一下westond应用是怎么渲染然后上屏的
直接上simple-dmabuf-egl的源码吧
int
main(int argc, char **argv)
{
....
display = create_display(drm_render_node, format, opts); // 创建display
....
window = create_window(display, window_size, window_size, opts); // 创建window,然后配置
....
if (!window->wait_for_configure)
redraw(window, NULL, 0); // 渲染
}
static struct display *
create_display(char const *drm_render_node, uint32_t format, int opts)
{
....
display->display = wl_display_connect(NULL); // 类似于EGL创建display
// 注册监听
display->registry = wl_display_get_registry(display->display);
wl_registry_add_listener(display->registry,
®istry_listener, display);
wl_display_roundtrip(display->display);
....
// 启动gbm(帧存管理相关),主要是打开DRM设备节点,创建gbm设备,这里开始跟GPU硬件厂商关联起来了
if (!display_set_up_gbm(display, drm_render_node))
goto error;
// 启动egl(上屏相关),主要是创建上下文,查询拓展支持情况,加载一些EGL相关的函数表
if (!display_set_up_egl(display))
goto error;
// 查询是否支持dma buffer,必须得支持,否则share memory无法做到
if (!display_update_supported_modifiers_for_egl(display))
goto error;
....
}
static struct window *
create_window(struct display *display, int width, int height, int opts)
{
....
// 启动gl(渲染相关),
if (!window_set_up_gl(window))
goto error;
....
}
// 通过OpenGL渲染到之前EGL创建的surface里面,其实可以简单得把weston应用理解成一个3D应用
static bool
window_set_up_gl(struct window *window)
{
GLuint vert = create_shader(
window->render_mandelbrot ? vert_shader_mandelbrot_text :
vert_shader_text,
GL_VERTEX_SHADER);
GLuint frag = create_shader(
window->render_mandelbrot ? frag_shader_mandelbrot_text :
frag_shader_text,
GL_FRAGMENT_SHADER);
window->gl.program = create_and_link_program(vert, frag);
glDeleteShader(vert);
glDeleteShader(frag);
window->gl.pos = glGetAttribLocation(window->gl.program, "pos");
window->gl.color = glGetAttribLocation(window->gl.program, "color");
glUseProgram(window->gl.program);
window->gl.offset_uniform =
glGetUniformLocation(window->gl.program, "offset");
window->gl.reflection_uniform =
glGetUniformLocation(window->gl.program, "reflection");
return window->gl.program != 0;
}
当一次渲染完成后,怎么把这次渲染的内容,提交给到Wayland server端(compositor)呢?
static void
redraw(void *data, struct wl_callback *callback, uint32_t time)
{
....
if (window->render_mandelbrot)
render_mandelbrot(window, buffer);
else
render(window, buffer); // 渲染
....
wl_surface_attach(window->surface, buffer->buffer, 0, 0); // buffer跟surface建立联系
wl_surface_damage(window->surface, 0, 0, window->width, window->height); // 通知compositor某个部分已经发生损坏,需要更新内容
....
wl_surface_commit(window->surface); // 提交surface,告诉compositor可以真正上屏了
....
}
上面三个操作client端和compositor端对应关系
client端 | compositor端 |
---|---|
wl_surface_attach | weston_surface_attach |
wl_surface_damage | weston_surface_damage |
wl_surface_commit | weston_surface_commit |
其实关键的地方主要是weston_surface_damage和weston_surface_commit
weston_surface_damage
idle_repaint -> weston_output_schedule_repaint_restart -> weston_output_damage
static enum weston_surface_status
weston_surface_commit(struct weston_surface *surface)
{
enum weston_surface_status status;
status = weston_surface_commit_state(surface, &surface->pending);
if (status & WESTON_SURFACE_DIRTY_SUBSURFACE_CONFIG)
weston_surface_commit_subsurface_order(surface);
weston_surface_schedule_repaint(surface);
return status;
}
weston_surface_commit -> weston_surface_schedule_repaint -> weston_output_schedule_repaint
WL_EXPORT void
weston_output_schedule_repaint(struct weston_output *output)
{
....
output->repaint_status = REPAINT_BEGIN_FROM_IDLE; // 这个是关键
assert(!output->idle_repaint_source);
output->idle_repaint_source = wl_event_loop_add_idle(loop, idle_repaint,
output);
....
}
static void
idle_repaint(void *data)
{
struct weston_output *output = data;
int ret;
assert(output->repaint_status == REPAINT_BEGIN_FROM_IDLE); // 跟上面对上了
output->repaint_status = REPAINT_AWAITING_COMPLETION;
output->idle_repaint_source = NULL;
ret = output->start_repaint_loop(output);
if (ret == -EBUSY)
weston_output_schedule_repaint_restart(output);
else if (ret != 0)
weston_output_schedule_repaint_reset(output);
}
// 这么长一个流程
weston_output_schedule_repaint_restart -> output_repaint_timer_arm
-> output_repaint_timer_handler -> weston_output_maybe_repaint
-> gl_renderer_repaint_output -> repaint_views
-> draw_paint_node -> repaint_region
repaint_region 分为pixmap和OpenGL两种
static void
repaint_region(struct gl_renderer *gr,
struct weston_paint_node *pnode,
pixman_region32_t *region,
pixman_region32_t *surf_region,
const struct gl_shader_config *sconf)
{
....
nfans = texture_region(pnode, region, surf_region); // 通过OpenGL贴纹理的形式贴上去
....
for (i = 0, first = 0; i < nfans; i++) {
glDrawArrays(GL_TRIANGLE_FAN, first, vtxcnt[i]);
if (gr->fan_debug)
triangle_fan_debug(gr, sconf, output, first, vtxcnt[i]);
first += vtxcnt[i];
}
....
}