☰
weston源码分析(三)硬件plane分配,硬件HWC合成,GPU合成。及送显。概述
2026/10/18 10:59:26 网站建设 项目流程

1. 从一次花屏说起:weston 合成路径到底怎么选

weston 是 Wayland 参考合成器里最常被拿来做嵌入式显示适配的一套实现,它跑在 DRM/KMS 之上,把每个 client 的 surface 组织成 view,再决定这些 view 是交给硬件 plane 直接扫描输出,还是先由 GPU 渲染成一张 framebuffer 再送显。你如果做过 MTK、瑞芯微、全志这类平台的显示调试,大概率遇到过这样的现象:日志里明明分配了 overlay plane,屏幕却出现撕裂、花屏,或者某个图层突然消失。这类问题十有八九出在合成策略的选择上,也就是 DRM plane 分配、HWC 硬件合成与 GPU 合成这三条路径的切换逻辑。

这篇聚焦 weston 源码里drm_assign_planes这条主线,把 plane 分配、硬件合成、GPU 合成兜底、最终送显串成一条完整的时序链。适合正在做嵌入式显示栈移植、或者需要排查合成异常的同学。我会给出可以直接复制的 DRM plane 状态查询命令、合成路径的日志开关配置,以及逐帧验证合成方式与送显结果的检查动作。核心检索词就是 weston plane 分配与 HWC/GPU 合成路径,读完你应该能自己判断某一帧到底走了哪条路。

先建立一个整体印象:DRM 里有三类 plane,cursor 主要给鼠标光标用,primary 是每个 CRTC 的主扫描面,overlay 一般靠硬件叠加在 primary 之上实现硬件合成,能省功耗,有的平台还能通过 zpos 把它压到 primary 下面。以 MTK8676 为例,每个输出设备只有一个 primary 和一个 overlay,没有 cursor plane。weston 的 drm backend 初始化时会根据 scanout plane 的宽高和格式创建 gbm surface,GPU 合成后的 fb 最终会和 scanout plane 关联,也就是送到 primary 上显示。理解了这套硬件约束,再看代码里的分配逻辑就顺了。

2. TaoToken 前置:把模型对话接进你的调试工作流

分析 weston 源码时,我经常需要一边读drm_output_propose_state的分支,一边查 DRM 属性、pixman region 的语义,还要对照不同平台的 plane 能力。这种场景下,把模型对话接进工作流能省不少来回翻文档的时间。TaoToken 提供的是统一的模型调用入口,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api ,不带 UTM。

你需要先拿到 API Key,入口在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。拿到之后,模型对话的调试入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。如果你打算长期做编码和 Agent 类任务,可以看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。控制台在 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。

这里要强调一点:TaoToken 是模型调用入口,不是编辑器替代品,也不是让你跳过源码阅读的捷径。它的价值在于,当你卡在drm_output_find_plane_for_view的某个条件判断上时,可以快速把相关代码片段贴进去问清楚语义,而不是在多个文档站之间反复横跳。下面给一个最小可用的调用示例,用 curl 就能验证连通性,把 Key 换成你自己的即可。

export TAOTOKEN_API_KEY="sk-你的key" curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "user", "content": "解释 weston 中 DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY 与 MIXED 的区别"} ] }'

返回里如果能看到choices数组和正常的content,说明链路通了。这一步只是把工具准备好,真正的主角还是 weston 的合成逻辑。

3. 可复制配置:plane 分配与合成路径的日志开关

要看清 weston 到底怎么分配 plane,第一步是把 DRM backend 的调试日志打开。weston 的日志级别通过--debug启动参数控制,DRM backend 内部用drm_debug宏输出 plane 分配细节。你可以在启动 weston 时加上调试参数,或者直接改配置文件。

先看启动方式,最直接的是命令行:

weston --backend=drm-backend.so --debug --log=/tmp/weston-drm.log

--debug会把drm_debug的输出打开,drm_assign_planes、drm_output_propose_state、drm_output_find_plane_for_view里的关键分支都会打到日志里。如果你用的是 systemd 管理 weston,可以改 service 文件里的ExecStart,把--debug加进去。

更细粒度的控制可以走 weston 的配置文件weston.ini,在[core]段里设置日志:

[core] log=/tmp/weston-drm.log debug=1

不过要注意,debug=1打开的是全局调试,输出量很大,建议只在排查阶段用。真正定位 plane 分配时,我更推荐直接看drm_debug打出来的那几行,它们会明确告诉你某个 view 是被分配到 plane 还是被 force 到 renderer。

接下来是 DRM plane 状态查询。内核的 DRM 子系统通过 debugfs 暴露了每个 plane 的当前状态,你可以直接读:

# 查看所有 plane 的 id、crtc、fb、格式和位置 cat /sys/kernel/debug/dri/0/plane-*/state # 更紧凑的汇总,看每个 plane 是否被占用 for p in /sys/kernel/debug/dri/0/plane-*; do echo "== $p ==" cat "$p/state" 2>/dev/null done

如果 debugfs 没挂载,先执行:

mount -t debugfs none /sys/kernel/debug

读plane-*/state时重点看几个字段:crtc是否非零(表示这个 plane 正在被某个 CRTC 使用)、fb是否非零(关联的 framebuffer)、src和dst的坐标(判断叠加位置)、format(判断格式是否匹配)。当你怀疑某个 overlay 没被用上时,对比 weston 日志里说“分配到 overlay”和实际plane-*/state里crtc是否真的被设置,就能确认分配是否落地。

还有一个容易被忽略的点:weston 的 DRM backend 在初始化时会根据 scanout plane 创建 gbm surface,这个 surface 的格式和 modifier 必须和 primary plane 支持的一致。如果格式不匹配,GPU 合成出来的 fb 可能无法关联到 scanout plane,最终表现为送显失败或黑屏。你可以在日志里搜gbm_surface_create和drmModeAddFB相关的行来确认。

4. 验证请求与成功结果:逐帧确认合成方式

配置好日志后,怎么确认某一帧到底走了硬件合成还是 GPU 合成?核心是看drm_assign_planes的决策输出。weston 处理 view 的顺序是从上到下,drm_output_propose_state会先尝试DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY模式,也就是完全走硬件合成。这种模式下,总共只有两个 paint_node(即 weston_view),最上面第一个符合条件的 paint_node 会被分配到 overlay plane,然后把它的 fb(graphicbuffer 句柄)和该 plane 关联。

如果 paint_node 数量大于 2,或者硬件 plane 不满足条件(比如不支持该 view 的几何变换、view 不是 dmabuf 等),就会退到DRM_OUTPUT_PROPOSE_STATE_MIXED模式,混合走 GPU 合成和硬件合成。同样会给最上面第一个符合条件的 view 分配 overlay plane,剩下的交给 GPU 渲染。

这里有个关键问题:如果最上面第一个符合送 overlay 条件的 view,并不是显示在最上层的第一个 view,weston 怎么处理?源码里有一段注释和判断很能说明问题:

/* Since we process views from top to bottom, we know that if * the view intersects the calculated renderer region, it must * be part of, or occluded by, it, and cannot go on a plane. */ pixman_region32_intersect(&surface_overlap, &renderer_region, &clipped_view); if (pixman_region32_not_empty(&surface_overlap)) { drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " "(occluded by renderer views)\n", ev); force_renderer = true; } pixman_region32_fini(&surface_overlap);

这段逻辑的意思是:因为 view 是从上到下处理的,如果当前 view 和已经计算出的 renderer region 有交集,说明它要么是 renderer region 的一部分,要么被它遮挡,那就不能放到 plane 上,只能强制走 renderer(GPU 合成)。这解释了为什么有时候你看到某个 view 明明格式和变换都满足,却没被分配到 overlay——它被上面的 GPU 合成区域盖住了。

验证时,你可以在日志里搜not assigning view和assigning view两类行,统计每一帧有多少 view 走了 plane、多少走了 renderer。成功的结果应该是:日志里明确出现assigning view ... to plane,同时/sys/kernel/debug/dri/0/plane-*/state里对应 plane 的crtc和fb被正确设置,屏幕输出无撕裂、无图层丢失。

送显时序上,GPU 合成完成后,weston 会调用drmModeAtomicCommit把 plane 状态一次性提交给内核,内核在下一个 vblank 生效。你可以用drm_info或者读crtc-*/state确认 commit 是否成功。如果 commit 返回-EBUSY或-EINVAL,通常是 plane 状态冲突或格式不支持,需要回到分配逻辑排查。

5. 常见错排查:401、local proxy failed 与 reading choices

调试过程中,除了 weston 本身的合成问题,调用模型接口时也会遇到一些典型报错。这里对照几个真实错误说清楚。

第一个是401 Unauthorized。这通常出现在你调用https://taotoken.net/api/v1/chat/completions时 Key 没带对或者过期了。检查Authorization: Bearer $TAOTOKEN_API_KEY这一行,确认 Key 是从 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 拿到的,并且没有多余空格。如果 Key 没问题还是 401,看下是不是把 API 端点写成了带 UTM 的地址,API 端点应该是 https://taotoken.net/api ,不带查询参数。

第二个是local proxy failed。这个报错一般出现在你本地有网络层拦截或者环境变量里配了HTTP_PROXY/HTTPS_PROXY,导致请求没发出去。先检查环境变量:

env | grep -i proxy

如果有输出,临时清掉再试:

unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy

第三个是reading choices相关的解析错误,比如json: cannot unmarshal ... reading choices。这通常是你把返回体当成了非 JSON 处理,或者返回体本身不是预期的结构。先确认返回的 HTTP 状态码是 200,再把原始 body 打出来看:

curl -s -o /tmp/resp.json -w "%{http_code}\n" https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"claude-sonnet-4-20250514","messages":[{"role":"user","content":"ping"}]}' cat /tmp/resp.json

如果 body 里是choices数组,说明结构正常,问题在你的解析代码;如果 body 是错误信息,按错误信息排查。

第四个是 OAuth 相关报错。如果你用的是 Claude Code 这类工具,配置里需要写全三件套:Base URL、Key、Model ID。Base URL 填 https://taotoken.net/api ,Key 填你的 API Key,Model ID 填你实际要用的模型名。缺任何一个都会导致鉴权失败。Claude Code 的接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的配置示例。

排查 weston 合成问题时,还有一个高频坑:overlay plane 的 zpos 设置。有的平台 overlay 默认在 primary 之上,但如果你手动改了 zpos 把它压到 primary 下面,硬件合成结果可能和预期相反。检查/sys/kernel/debug/dri/0/plane-*/state里的zpos字段,确认它符合你的图层顺序预期。

6. 把合成路径串起来:从 plane 分配到送显的完整链路

回到主线,把整条链路再串一遍。weston 的 DRM backend 初始化时,根据 scanout plane(primary)的宽高和格式创建 gbm surface,这是最终送显的载体。每一帧,drm_assign_planes被调用,它通过drm_output_propose_state和drm_output_find_plane_for_view决定每个 view 的去向。

决策顺序是:先尝试PLANES_ONLY全硬件合成,从上到下处理 view,第一个符合条件的分配到 overlay plane,fb 与 plane 关联。如果 view 数量超过 2 或有条件不满足,退到MIXED模式,混合 GPU 和硬件合成。处理过程中,如果当前 view 和已计算的 renderer region 有交集,就被强制走 renderer,这就是那段pixman_region32_intersect判断的作用。

GPU 合成完成后,生成的 fb 会和 scanout plane 关联,最终通过 atomic commit 在下一个 vblank 送显。验证时,日志看assigning view和not assigning view,内核看plane-*/state的crtc/fb/zpos,两边对上就说明分配和送显一致。

如果你在排查中发现某个 overlay 始终没被用上,先确认 view 是不是 dmabuf、几何变换是否被 plane 支持、有没有被上面的 renderer region 遮挡。这三点覆盖了绝大多数“plane 分配了但没生效”的情况。把日志开关和 debugfs 查询配合起来用,逐帧对照,合成路径的选择就不再是黑盒。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询