Wayland源码分析-repaint相关流程

本文解析了Wayland中repaint流程的关键作用及其具体实现过程。重点介绍了在Wayland架构下,客户端完成绘图后如何通过commit操作提交给compositor,并最终通过repaint流程将更新内容提交给compositor进行合成,确保内容正确显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文关注Wayland中绘图相关流程,这是wayland中非常关键的流程之一。

repaint?

为什么wayland中要有repaint操作呢?不是号称都是client绘图、wayland只负责合成么?

确实是Client绘图,compositor(服务端)只负责合成。但由于client绘图实际实现为double buffer,client最初的绘图操作都是在pending buffer(也就是我们理解的back buffer)中进行的,并没有直接绘制到framebuffer中,所以,在client完成绘制后,需要进行commit操作(前面的文章已经介绍过),commit后,服务端会将相应的buffer会更新到当前的surface中,此时,需要进行repaint操作,将客户端相应的绘制内容最终拷贝到framebuffer中。

这里的repaint操作,本质上只是将客户端更新的绘制内容(damage区域)提交给compositor,最后由compositor进行合成后绘制到界面上。

所以,本质上,还是client绘图,compositor进行合成,没有矛盾。

流程

以weston实现代码分析,从surface_commit开始到repaint的简要流程为:

surface_commit
  weston_surface_commit
    weston_surface_schedule_repaint

再继续看看weston_surface_schedule_repaint代码实现:

WL_EXPORT void
weston_surface_schedule_repaint(struct weston_surface *surface)
{
	struct weston_output *output;

	wl_list_for_each(output, &surface->compositor->output_list, link)
		if (surface->output_mask & (1 << output->id))
			weston_output_schedule_repaint(output);
}

很简单,只是对output_list中的所有output调用weston_output_schedule_repaint操作。

这里的output对应一个输出设备,比如显示器、远程桌面等,这些output是在compositor的初始化过程中创建的,先看看这个初始化流程。

output初始化

其中涉及到两个关键的地方:

  1. weston支持在不同的backend(后端)上运行,包括drm、wayland和X11。这里需要注意这集中后端的区别,尤其是wayland后端,很容易弄混。
  • drm后端,是wayland环境中默认使用的后端,其使用Linux KMS输出,使用evdev作为输入,实际就是利用drm的接口实现合成。利用了硬件加速。
  • wayland后端,这是指让weston运行于另一个wayland compositor(比如另一个weston,嵌套运行)之上,作为wayland的客户端运行,此时的weston实例表现为另一个compositor上的一个窗口。注意:这个不是默认的方式,这是嵌套方式,使用较少。
  • X11后端,即weston运行在X11之上,每个weston output作为一个X window。这种方式对于weston的测试非常有用(可以在现有的Xorg环境中测试weston的功能)。
  1. 使用特定后端的情况下,绘图时,支持使用不同的renderer(绘图引擎),比如OpenGL(EGL,硬件加速)或者时pixman(软件绘制),使用wayland作为后端时,默认使用OpenGL作为renderer。

这两点的实现,都需要进行抽象、分层,以便于隔离,减少耦合,比较常见的实现手法,函数指针(回调函数)(对应于OO语言(如C++、Java)中的多态)。典型的依赖倒置原则。

对于关键点1的实现还有点不一样(虽然思想是类似的),其实现为在运行时动态加载共享库,使用其中的接口,简单说,就是用dlopen,然后每个backend实现为相应的动态库。相应流程为:

main ->
  load_backend ->
    load_drm_backend ->
      weston_compositor_load_backend ->
        weston_load_module

weston_load_module实现为:

WL_EXPORT void *
weston_load_module(const char *name, const char *entrypoint)
{
	char path[PATH_MAX];
	void *module, *init;

	if (name == NULL)
		return NULL;

	if (name[0] != '/')
		snprintf(path, sizeof path, "%s/%s", MODULEDIR, name);
	else
		snprintf(path, sizeof path, "%s", name);

	module = dlopen(path, RTLD_NOW | RTLD_NOLOAD);
	if (module) {
		weston_log("Module '%s' already loaded\n", path);
		dlclose(module);
		return NULL;
	}

	weston_log("Loading module '%s'\n", path);
	module = dlopen(path, RTLD_NOW);
	if (!module) {
		weston_log("Failed to load module: %s\n", dlerror());
		return NULL;
	}

	init = dlsym(module, entrypoint);
	if (!init) {
		weston_log("Failed to lookup init function: %s\n", dlerror());
		dlclose(module);
		return NULL;
	}

	return init;
}

代码简单直接。

上述关键点2,实现体现在了output的初始化流程中,相关主要流程为:

main
  load_backend->
    load_drm_backend->
      drm_backend_output_configure->
        weston_output_enable->
          output->enable->
            drm_output_enable

看看drm_output_enable的实现:

static int
drm_output_enable(struct weston_output *base)
{
	struct drm_output *output = to_drm_output(base);
	struct drm_backend *b = to_drm_backend(base->compositor);
	struct weston_mode *m;

	output->dpms_prop = drm_get_prop(b->drm.fd, output->connector, "DPMS");
  //设置renderer
	if (b->use_pixman) {
    //使用pixman软件绘制
		if (drm_output_init_pixman(output, b) < 0) {
			weston_log("Failed to init output pixman state\n");
			goto err_free;
		}
    //使用egl,即openGL接口
	} else if (drm_output_init_egl(output, b) < 0) {
		weston_log("Failed to init output gl state\n");
		goto err_free;
	}

  ...
  // 设置start_repaint_loop接口,在commit后的repaint流程中调用
	output->base.start_repaint_loop = drm_output_start_repaint_loop;
  // 设置repaint接口,用于最终绘制
	output->base.repaint = drm_output_repaint;
	output->base.assign_planes = drm_assign_planes;
	output->base.set_dpms = drm_set_dpms;
	output->base.switch_mode = drm_output_switch_mode;
  ...
}

继续repaint流程

从weston_surface_schedule_repaint开始,

weston_surface_schedule_repaint->
  weston_output_schedule_repaint

weston_output_schedule_repaint实现如下:

WL_EXPORT void
weston_output_schedule_repaint(struct weston_output *output)
{
  ...
  //将idle_repaint加入idle loop事件中,当服务端没有其它epoll事件时,会执行idle_repaint
	wl_event_loop_add_idle(loop, idle_repaint, output);
  ...
}

idle_repaints实现如下:

static void
idle_repaint(void *data)
{
	struct weston_output *output = data;
  //调用在前面wayland_output_create初始化的接口,由于默认使用drm_output_start_repaint_loop
	output->start_repaint_loop(output);
}

drm_output_start_repaint_loop之后的主要流程如下:

drm_output_start_repaint_loop->
  weston_output_finish_frame->
    output_repaint_timer_handler->
      weston_output_repaint->
      output->repaint()-> // 这里的repaint为之前output初始化时初始化的drm_output_repaint
        drm_output_repaint

drm_output_repaint利用modsetting接口将绘制的内容最终显示到屏幕上,其机制还比较复杂,主要利用了pageflip、vblank和plane,相关原理与drm的API编程强相关,内容还比较多,这里就暂时不深入了,以后抽空单独写相关的文章来说明。

 

转自:http://happyseeker.github.io/graphic/2016/11/11/wayland-repaint-relative-flow.html

<think>我们正在Yocto环境中构建QtWayland,并且遇到了配置错误,因为预条件不满足导致'wayland-vulkan-server-buffer'特性无法启用。根据错误信息,我们决定在构建配置中禁用该特性。 在Yocto中,我们通常通过修改配方(recipe)来调整构建选项。对于QtWayland,我们可以通过添加一个补丁文件或直接修改配方中的配置参数来禁用特定特性。 错误信息中显示,配置命令中包含了`-feature-wayland-vulkan-server-buffer`选项,这导致配置失败。因此,我们需要将其改为`-no-feature-wayland-vulkan-server-buffer`。 查看错误日志中的配置命令: ``` ... -- -no-feature-wayland-brcm -feature-wayland-client ... -feature-wayland-vulkan-server-buffer ... ``` 我们需要将`-feature-wayland-vulkan-server-buffer`替换为`-no-feature-wayland-vulkan-server-buffer`。 在Yocto中,我们可以通过以下方式实现: 1. **创建一个补丁文件**:修改QtWayland的配置参数,将启用vulkan-server-buffer改为禁用。 2. **在配方中追加配置选项**:通过修改配方文件,在配置命令中增加`-no-feature-wayland-vulkan-server-buffer`并移除原有的启用选项。 但是,由于配方中已经通过变量传递了配置选项,我们可以通过覆盖配置选项来实现。 在`qtwayland_git.bb`配方中,通常会有一个变量(如`PACKAGECONFIG`或`EXTRA_QMAKEVARS_PRE`)用于传递配置选项。我们需要检查配方并覆盖相关选项。 由于我们无法直接修改原始配方(因为它在meta-qt5层中),我们可以通过以下两种方式之一: ### 方法1:在本地层中为qtwayland创建一个bbappend文件 步骤: 1. 在您自己的层(例如`meta-custom`)中创建对应的bbappend文件: ``` mkdir -p /path/to/your/meta-custom/recipes-qt/qt5 touch /path/to/your/meta-custom/recipes-qt/qt5/qtwayland_%.bbappend ``` 2. 在该bbappend文件中,我们可以通过修改`EXTRA_QMAKEVARS_PRE`变量来添加`-no-feature-wayland-vulkan-server-buffer`,并移除原有的`-feature-wayland-vulkan-server-buffer`。但是注意,在原始配方中,配置选项可能是通过变量传递的,因此我们需要覆盖这些变量。 另一种方法是直接覆盖`EXTRA_QMAKEVARS_PRE`变量,将不需要的选项过滤掉,然后添加我们需要的选项。 查看原始配方(`qtwayland_git.bb`)中关于配置选项的部分,通常会有类似: ``` PACKAGECONFIG ?= "..." ``` 或者 ``` EXTRA_QMAKEVARS_PRE += "..." ``` 在错误日志中,我们看到配置命令中包含了: ``` -no-feature-wayland-brcm -feature-wayland-client -no-feature-wayland-drm-egl-server-buffer -feature-wayland-egl -no-feature-wayland-libhybris-egl-server-buffer -feature-wayland-server -feature-wayland-vulkan-server-buffer -no-feature-xcomposite-egl -no-feature-xcomposite-glx ``` 因此,我们可以在bbappend文件中覆盖`EXTRA_QMAKEVARS_PRE`,去掉`-feature-wayland-vulkan-server-buffer`并添加`-no-feature-wayland-vulkan-server-buffer`。 但是,由于原始配方可能通过变量设置这些选项,我们可以通过字符串替换来实现。不过,更简单的方法是在bbappend文件中直接修改配置选项。 例如,我们可以这样写bbappend文件: ``` # 首先,包含原始配方的配置 include qtwayland_git.bb # 然后,修改EXTRA_QMAKEVARS_PRE,将原来的-feature-wayland-vulkan-server-buffer替换为-no-feature-wayland-vulkan-server-buffer # 但是,由于EXTRA_QMAKEVARS_PRE是一个字符串,我们可以使用替换函数 # 注意:如果原始配方中并没有直接使用EXTRA_QMAKEVARS_PRE,而是通过其他变量,我们需要覆盖那个变量 # 另一种方法:直接重新定义配置选项 # 由于原始配方中可能通过PACKAGECONFIG来设置,我们可以覆盖PACKAGECONFIG # 如果原始配方中有: # PACKAGECONFIG[vulkan] = "-feature-wayland-vulkan-server-buffer,-no-feature-wayland-vulkan-server-buffer" # 那么我们可以通过设置PACKAGECONFIG_remove = "vulkan"来移除vulkan支持,这样就会使用-no-feature-wayland-vulkan-server-buffer # 但是,查看原始配方(在meta-qt5中)发现,它并没有使用PACKAGECONFIG来控制vulkan-server-buffer,而是硬编码在配置命令中。 # 因此,我们只能通过修改EXTRA_QMAKEVARS_PRE来实现。 # 在原始配方中,配置选项是通过EXTRA_QMAKEVARS_PRE传递的,所以我们可以在bbappend中覆盖它。 # 首先,我们获取原始的EXTRA_QMAKEVARS_PRE,然后进行字符串替换 EXTRA_QMAKEVARS_PRE:remove = "-feature-wayland-vulkan-server-buffer" EXTRA_QMAKEVARS_PRE += "-no-feature-wayland-vulkan-server-buffer" ``` 但是,注意:`EXTRA_QMAKEVARS_PRE`是一个字符串变量,我们使用`:remove`操作符来移除一个字符串片段,然后添加新的选项。 但是,`:remove`操作符是用于列表的,而字符串也可以视为由空格分隔的列表。因此,我们可以这样写: ``` EXTRA_QMAKEVARS_PRE:remove = " -feature-wayland-vulkan-server-buffer" EXTRA_QMAKEVARS_PRE += " -no-feature-wayland-vulkan-server-buffer" ``` 注意空格,确保移除的字符串是独立的选项。 但是,由于原始字符串中可能有多个选项,我们也可以直接重新定义整个配置选项。但这样可能不灵活。 另一种方法:在bbappend文件中,我们使用: ``` do_configure:prepend() { # 将配置选项中的-feature-wayland-vulkan-server-buffer替换为-no-feature-wayland-vulkan-server-buffer sed -i 's/-feature-wayland-vulkan-server-buffer/-no-feature-wayland-vulkan-server-buffer/g' ${S}/configure.pri } ``` 但这种方法侵入性较强,且依赖于原始配方的内部文件。 ### 方法2:通过修改本地配置文件(推荐) 在Yocto中,我们可以在`local.conf`或自定义的distro配置中,通过添加全局变量来覆盖配置选项。但是,这通常针对整个构建环境,可能不灵活。 ### 实际解决方案 根据错误日志,配置命令中包含了`-feature-wayland-vulkan-server-buffer`,这个选项来自于哪里?在`qtwayland_git.bb`配方中,我们可以找到: 在`meta-qt5/recipes-qt/qt5/qtwayland_git.bb`中,有: ``` EXTRA_QMAKEVARS_PRE += " \ CONFIG+=${PACKAGECONFIG_CONFARGS} \ ${@bb.utils.contains('PACKAGECONFIG', 'xcomposite-glx', '', 'CONFIG+=no-xcomposite-glx', d1', d)} \ ${@bb.utils.contains('PACKAGECONFIG', 'xcomposite-egl', '', 'CONFIG+=no-xcomposite-egl', d)} \ " ``` 而`PACKAGECONFIG_CONFARGS`又由`PACKAGECONFIG`变量控制。因此,我们可以在bbappend文件中通过修改`PACKAGECONFIG`来移除vulkan支持。 但是,在原始配方中,`PACKAGECONFIG`并没有包含vulkan选项。所以,我们只能通过覆盖`EXTRA_QMAKEVARS_PRE`来手动修改。 ### 最终bbappend文件内容 在您的层中创建文件`recipes-qt/qt5/qtwayland_%.bbappend`,内容如下: ``` # 移除原有的-feature-wayland-vulkan-server-buffer选项,并添加-no-feature-wayland-vulkan-server-buffer # 注意:使用空格作为分隔,确保移除整个选项 EXTRA_QMAKEVARS_PRE:remove = " -feature-wayland-vulkan-server-buffer" EXTRA_QMAKEVARS_PRE += " -no-feature-wayland-vulkan-server-buffer" ``` ### 验证 构建时,配置命令中应该不再有`-feature-wayland-vulkan-server-buffer`,而是替换为`-no-feature-wayland-vulkan-server-buffer`。 ### 注意事项 1. 确保bbappend文件的路径和命名正确:`recipes-qt/qt5/qtwayland_%.bbappend` 2. 清除QtWayland的构建缓存,重新构建: ``` bitbake -c cleansstate qtwayland && bitbake qtwayland ``` 这样,应该可以解决配置错误。 ### 相关问题 1. 如何在Yocto中为其他Qt组件添加自定义配置选项? 2. 如何查看Yocto中某个配方的变量值(如EXTRA_QMAKEVARS_PRE)? 3. 在Yocto中,bbappend文件的工作原理是什么? 4. 除了bbappend,还有哪些方法可以修改Yocto配方?</think>### 在 Yocto 中禁用 Qt Wayland 的 Vulkan Server Buffer 功能 #### 解决方案 在 Yocto 构建系统中禁用 `wayland-vulkan-server-buffer` 功能,需要通过修改 QtWayland 的配方配置实现。以下是具体步骤: ##### 1. 创建或修改 bbappend 文件 在您的自定义层中创建或修改 QtWayland 的配方附加文件: ```bash # 创建配方目录 mkdir -p /path/to/your/meta-layer/recipes-qt/qt5 # 创建或编辑 bbappend 文件 vi /path/to/your/meta-layer/recipes-qt/qt5/qtwayland_%.bbappend ``` ##### 2. 添加配置覆盖 在 bbappend 文件中添加以下内容,显式禁用 Vulkan server buffer: ```bitbake # 禁用 vulkan-server-buffer 功能 EXTRA_QMAKEVARS_PRE += "-no-feature-wayland-vulkan-server-buffer" # 可选:同时禁用相关依赖检查 PACKAGECONFIG:remove = "vulkan-server-buffer" ``` ##### 3. 验证配置更改 构建前检查配置是否生效: ```bash bitbake -e qtwayland | grep ^EXTRA_QMAKEVARS_PRE # 应输出包含 "-no-feature-wayland-vulkan-server-buffer" ``` ##### 4. 清理并重新构建 ```bash bitbake -c cleansstate qtwayland bitbake qtwayland ``` #### 关键原理说明 1. **EXTRA_QMAKEVARS_PRE** 变量直接传递给 qmake 配置命令 2. **-no-feature-** 前缀显式禁用指定功能[^1] 3. **PACKAGECONFIG:remove** 确保不包含相关依赖 4. 修改效果:配置摘要中应显示: ``` Vulkan-based server buffer integration . no ``` #### 备用方案(直接修改配方) 如果 bbappend 不生效,可直接修改原始配方: ```bitbake # 在 qtwayland_git.bb 中查找配置行 sed -i '/EXTRA_QMAKEVARS_PRE/ s/$/ -no-feature-wayland-vulkan-server-buffer/' \ /path/to/meta-qt5/recipes-qt/qt5/qtwayland_git.bb ``` > **注意**:此修改在配方升级时会被覆盖,建议优先使用 bbappend 方式[^1]。 #### 验证构建成功标志 检查 log.do_configure 文件: ``` Configure summary: ... Vulkan-based server buffer integration . no ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值