weston源码分析(三)硬件plane分配,硬件HWC合成,GPU合成。及送显。概述
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 里有三类 planecursor 主要给鼠标光标用primary 是每个 CRTC 的主扫描面overlay 一般靠硬件叠加在 primary 之上实现硬件合成能省功耗有的平台还能通过 zpos 把它压到 primary 下面。以 MTK8676 为例每个输出设备只有一个 primary 和一个 overlay没有 cursor plane。weston 的 drm backend 初始化时会根据 scanout plane 的宽高和格式创建 gbm surfaceGPU 合成后的 fb 最终会和 scanout plane 关联也就是送到 primary 上显示。理解了这套硬件约束再看代码里的分配逻辑就顺了。2. TaoToken 前置把模型对话接进你的调试工作流分析 weston 源码时我经常需要一边读drm_output_propose_state的分支一边查 DRM 属性、pixman region 的语义还要对照不同平台的 plane 能力。这种场景下把模型对话接进工作流能省不少来回翻文档的时间。TaoToken 提供的是统一的模型调用入口官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 不带 UTM。你需要先拿到 API Key入口在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。拿到之后模型对话的调试入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。如果你打算长期做编码和 Agent 类任务可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。这里要强调一点TaoToken 是模型调用入口不是编辑器替代品也不是让你跳过源码阅读的捷径。它的价值在于当你卡在drm_output_find_plane_for_view的某个条件判断上时可以快速把相关代码片段贴进去问清楚语义而不是在多个文档站之间反复横跳。下面给一个最小可用的调用示例用 curl 就能验证连通性把 Key 换成你自己的即可。export TAOTOKEN_API_KEYsk-你的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 --backenddrm-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 debug1不过要注意debug1打开的是全局调试输出量很大建议只在排查阶段用。真正定位 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然后把它的 fbgraphicbuffer 句柄和该 plane 关联。如果 paint_node 数量大于 2或者硬件 plane 不满足条件比如不支持该 view 的几何变换、view 不是 dmabuf 等就会退到DRM_OUTPUT_PROPOSE_STATE_MIXED模式混合走 GPU 合成和硬件合成。同样会给最上面第一个符合条件的 view 分配 overlay plane剩下的交给 GPU 渲染。这里有个关键问题如果最上面第一个符合送 overlay 条件的 view并不是显示在最上层的第一个 viewweston 怎么处理源码里有一段注释和判断很能说明问题/* 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 上只能强制走 rendererGPU 合成。这解释了为什么有时候你看到某个 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_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 拿到的并且没有多余空格。如果 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 KeyModel ID 填你实际要用的模型名。缺任何一个都会导致鉴权失败。Claude Code 的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的配置示例。排查 weston 合成问题时还有一个高频坑overlay plane 的 zpos 设置。有的平台 overlay 默认在 primary 之上但如果你手动改了 zpos 把它压到 primary 下面硬件合成结果可能和预期相反。检查/sys/kernel/debug/dri/0/plane-*/state里的zpos字段确认它符合你的图层顺序预期。6. 把合成路径串起来从 plane 分配到送显的完整链路回到主线把整条链路再串一遍。weston 的 DRM backend 初始化时根据 scanout planeprimary的宽高和格式创建 gbm surface这是最终送显的载体。每一帧drm_assign_planes被调用它通过drm_output_propose_state和drm_output_find_plane_for_view决定每个 view 的去向。决策顺序是先尝试PLANES_ONLY全硬件合成从上到下处理 view第一个符合条件的分配到 overlay planefb 与 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 查询配合起来用逐帧对照合成路径的选择就不再是黑盒。