资讯动态

嵌入式Linux图形显示栈全解析:从DRM/KMS到Wayland与Qt实战

发布时间:2026/10/2 6:31:30 来源:尧图企业网站定制
画过屏幕点过灯的人都知道Linux图形显示这块在嵌入式开发里是最容易让人懵圈的部分之一。从APP到屏幕点亮中间的层多得像食堂打饭的排队窗口每一层都有自己的协议、缓冲区和“潜规则”。我在嵌入式分享第18期里专门把Linux图形显示这条链路从头到尾捋了一遍今天把这份干货整理成文希望能帮那些被/dev/fb0、DRM、Mesa、Wayland这些词绕晕的兄弟少走点弯路。这篇文章既适合刚转行做嵌入式Linux、天天对着 dmesg 和 modetest 不知所措的新手也适合已经在跑 Qt 或者 Wayland 合成器、但只知其然不知其所以然的进阶开发者。我会从内核态到用户态把整个图形栈的骨架、关键对象的职责、常见掉坑点、以及怎么在板子上一步步点亮屏幕和跑起合成器全部讲透。不搞学院派那套名词轰炸讲的全是我实际调试时验证过的逻辑。1. 内容整体设计与思路拆解1.1 为什么图形显示是嵌入式Linux的“深水区”嵌入式Linux的图形显示之所以难本质是因为它横跨了内核态和用户态两个世界而你平时只要在其中一个世界里呆过就很容易用另一个世界的思维去套结果必然卡壳。内核里负责的是寄存器、时钟、数据传输用户态负责的是图形API、缓冲区分配、事件循环。这两者之间用标准接口拼起来任何一个环节没对齐屏幕就是黑的。刚入门的时候很多人以为“显示”不外乎就是往/dev/fb0里写像素帧缓冲嘛一块内存写进去就能看到。这种思路在imx6ull、STM32MP1这类集成LCD控制器的板子上确实还能跑但一旦上了带GPU的SoC比如RK3588、i.MX8M Plus帧缓冲方案立刻就不够用了。因为它没有统一的协调机制去管理多图层叠加、旋转缩放、硬件光标更别提跟GPU之间做buffer共享了。这时候真正干活的其实是内核里的DRM/KMS子系统而不是那个看起来亲切的fbdev。所以我在这篇文章里刻意把结构按“从底层到上层、从内核到用户态”的顺序设计。先讲清楚DRM/KMS在底层是怎么管显示设备的再讲Mesa和libdrm在中间怎么搭桥最后才讲X11/Wayland这些显示服务器和Qt/GTK这些应用怎么落到屏幕上。这种顺序会让人先建立“硬件-驱动-抽象-服务-应用”的全景图后面排查问题才有方向感。1.2 从APP到屏幕这趟数据旅程到底经历了什么我一直喜欢用一个比喻来解释Linux图形显示的整套流程把屏幕想象成一块黑板APP里的每一帧画面其实是一张写满内容的透明胶片。胶片上画的不是最终显示的画面而是描述了“这个窗口应该长什么样”的数据。所有胶片叠在一起由一位“排版员”把它们合成一块完整的图案再投影到黑板上。这位排版员在Linux里就是显示服务器X11或Wayland里的合成器黑板上最终的内容就是帧缓冲。具体到技术链路上沿途有四大层应用层Qt/GTK应用进程通过EGL/Vulkan等API提交图形命令给GPU、图形库层Mesa负责把OpenGL/EGL命令翻译成GPU能懂的硬件指令、显示服务器层Wayland合成器或X Server负责合成各应用的画面决定哪块像素以什么位置和透明度呈现在屏幕上、内核显示驱动层DRM/KMS子系统真正控制显示控制器和物理屏幕把合成好的一帧画面调度到输出接口上。这一趟旅程里经常被人忽略却极其重要的是“共享内存”的概念。GPU画完的画面不是拷来拷去的而是通过DMA-BUF机制让显示控制器直接去读GPU写好的那块显存。如果哪一块没有用对Buffer共享方式而是傻乎乎地把画好的帧从显存拷贝到系统内存再到显存性能会惨不忍睹。好多项目移植Qt时发现刷新只有十几帧排查到最后往往是Buffer共享没走对走了额外的拷贝路径。2. 内核侧的核心DRM/KMS才是真正干活的2.1 DRM的四大对象CRTC、Encoder、Connector、Plane说到DRM首先得破除一个误解DRM不是“数字版权管理”在Linux图形栈里它是 Direct Rendering Manager中文叫直接渲染管理器。它不是一个用户态的库而是一套内核驱动框架。其中KMSKernel Mode Setting内核模式设置负责显示控制器的模式设置、帧缓冲管理、图层叠加。你如果跑过老一点的嵌入式系统会看到/dev/fb0和一个叫/dev/dri/card0的设备后者就是DRM设备节点。DRM/KMS里有四个核心对象我花了好长时间才彻底分清楚这里一次讲明白Plane图层最底层的显示单元。每个Plane代表显示控制器里的一层硬件图层可以设置它显示哪块帧缓冲、位置在哪里、大小是多少、有没有缩放、透明度怎么处理。简单的控制器可能只有一个主平面Primary Plane复杂一点的像瑞芯微的方案往往有好几个带缩放能力的硬件图层可以让你把一个视频层和一个UI层叠在一起而不需要软件混合。CRTC显示时钟控制器缩写源于老式CRT显示器它负责的是“时序”。屏幕的刷新要遵循一行一行扫、一帧一帧刷的节奏CRTC就是生成并管理这种节奏的硬件模块。每一个CRTC能把若干个Plane的画面混合并输出成一路信号。Encoder编码器把CRTC送过来的并行时序信号转换成某一种输出接口信号的模块。比如转成LVDS信号给屏幕、转成HDMI信号给电视、转成DSI信号给手机屏。Connector连接器物理层面的接口代表一个看得见摸得着的输出端口比如某个HDMI座子、某个LVDS排线座也就是屏幕实际“插”的地方。Connector上有EDID显示器的身份信息Linux启动时通过I2C读取它来知道屏幕支持哪些分辨率。这四个对象的连接关系用一句话概括就是Plane管内容CRTC管时序Encoder管信号格式Connector管物理世界。你调用KMS的ATOMIC接口实际上就是在原子地配置“哪块Buffer给哪个Plane哪个Plane给哪个CRTCCRTC输出到哪个EncoderEncoder接到哪个Connector”。整个链路设置好屏幕自然就亮了。2.2 帧缓冲、Page Flip与双缓冲的真实含义帧缓冲在DRM层面叫DUMB Buffer智能一点的叫GEM Buffer本质上就是一块内存里面装的是像素数据。但要注意这块内存和/dev/fb0时代那种固定映射到系统内存的Simple Buffer不一样DUMB Buffer的物理内存可能落在显存上访问路径也不同。我最早在DM3730那块板子上踩过坑以为DUMB Buffer还在系统总线上直接memcpy往里面写数据结果屏幕上什么也不出。实际上要先映射到用户空间再往映射后的虚拟地址上写这个映射过程还不是你想map哪块就map哪块得通过DRM的IOCTL来申请。Page Flip是DRM里最能够体现性能的操作。简单理解就是你有两个Buffer一个正在给屏幕扫描front buffer另一个在后台让GPU画back buffer。画面画好了你调用一次drmModePageFlip()驱动会在下一个垂直消隐期VBlank到来时把扫描的源从front切换成back瞬间完成。这次切换不会产生撕裂感因为切换时机是跟硬件刷新节奏严格同步的。双缓冲听着简单但在嵌入式项目里翻车也不少。最常见的是忘了做“翻转同步”GPU画完一帧就直接Flip结果前后两个Buffer被同一帧内容反复刷看起来就是画面一卡一卡或者开发人员没做Fence同步GPU还没画完就开始Flip屏幕上出现半帧撕裂。正确的做法是使用显式的同步机制在用户态用EGL或者Vulkan的Fence等待渲染完毕再发起Flip请求。三缓冲其实也是同一套逻辑只是多一层缓冲来缓解GPU和显示器的节奏差代价是多占一份显存。2.3 用modetest快速点亮一块屏幕的实战聊理论不如动手。很多人拿到一块新板子第一件事是把系统跑起来、看到Logo而调试图形栈的第一步也是验证KMS能不能正常点亮屏幕。我常用的命令是modetest它是libdrm自带的一个小工具用来枚举DRM设备和配置模式。# 列出所有的DRM设备 ls /dev/dri/ # 用modetest查看card0上的所有资源 modetest -M rk3568 -c modetest -M rk3568 -p其中-M可以指定驱动名-c显示所有Connector-p显示所有Plane。输出里会清楚地看到每个Connector支持哪些分辨率每个CRTC目前连了哪个Encoder每个Plane引用了哪个Buffer。如果你的板子有eDP输出、HDMI输出这里都会列出来。更狠一点可以直接用modetest做一次模式设置测试不需要写任何应用代码就能验证整条显示链路的底层通路是否正常# 显式指定连接器、分辨率、格式做一次flip测试 modetest -M rk3568 -s 42:1920x1080-60 -v如果执行这条命令后屏幕出现了一条彩条或者至少没有报错且dmesg里没有DRM相关的错误信息说明KMS驱动在底层是通的。如果失败最常见的两个原因是Connector编号不对或者屏幕实际EDID跟面板参数不匹配。我排查过很多次最后都是因为RG/BGR顺序配错导致颜色全乱这种问题modetest不一定能直接暴露出来得看实际画面颜色。3. 用户态中间层libdrm、Mesa与GPU的协同3.1 libdrm内核DRM接口的用户态门面内核的DRM接口是一组IOCTL调用但我们不会在应用层直接写ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, args)这种代码因为太底层格式容易错。libdrm把这堆IOCTL封装成了比较友好的C API比如drmModeGetConnector()、drmModeCreateDumbBuffer()、drmModePageFlip()。它是用户态访问DRM设备最基础的一层工具库很多上层组件——Mesa、Weston、X Server、Qt——最终都通过它跟内核打交道。嵌入式Linux里libdrm还附带了一些实用工具除了前面说的modetest之外还有drmdevice用来查看设备支持能力drmstat用来观察VBlank计数和Page Flip状态调试的时候非常好用。我在排查屏幕闪屏问题时就靠观察drmstat里VBlank的计数变化来判断翻页是否按预期节奏发生。要特别提醒一点libdrm有好几个版本。不同板子的SDK里libdrm的版本从2.4.50到2.4.120都有API有一些差异特别是ATOMIC接口相关的老API部分已经被标记废弃。如果照抄网上老外的示例代码在新内核上编译会报参数不对或者找不到符号这不是你的错是示例代码过时了。装环境时优先使用板子SDK配套的libdrm版本别盲目拉最新版尤其别混用。3.2 Mesa与DRM render nodeGPU渲染的幕后通道Mesa是Linux图形栈里最重要的一个开源库它实现了OpenGL、OpenGL ES、Vulkan等图形API并把这些API的调用翻译成GPU能执行的命令。在传统的嵌入式平台GPU厂商会提供闭源驱动但仍然会选择基于Mesa的某个分支来适配OpenGL ES的支持比如Rockchip的Mali GPU方案、Amlogic的方案底层都基于Mesa架构。Mesa跟DRM的关系可以从两个设备节点理解。一个DRM设备通常有两个节点/dev/dri/card0主要用于显示控制KMS操作和/dev/dri/renderD128主要用于GPU加速渲染算力操作。render node上的DRM接口专门用于分配GEM Buffer、提交命令缓冲、同步Fence但不做任何跟屏幕模式相关的操作。Mesa的gbm模块Generic Buffer Management通用缓冲管理会在render node上分配存储图形数据的缓冲区再共享给显示链路使用。这里有一个容易混淆的概念GBM和EGL。GBM负责“给GPU搭一块画画的画布”EGL负责“在这块画布上建立OpenGL ES的绘制上下文”。很多项目移植Qt的时候编译时缺了gbs、egl相关的组件报一屏错误其实就是GBM这个分配器没选对或者没有安装完整。我记得在做一个全志T507的项目时编译Qt时要打开-linuxfb或者后面的 EGLFS配置缺了GBMEGL支持屏幕上总是显示不出任何内容。3.3 嵌入式GPU方案取舍别被“支持OpenGL”忽悠嵌入式的GPU方案不像PC那么统一常见的有ARM的Mali系列、Imagination的PowerVR、高通的Adreno还有老一点的Vivante GC系列。每个方案的Mesa支持情况、KMS集成度、显存分配方式都不一样在做方案调研时不能只盯着“支持OpenGL ES 3.1”这一句话。最容易踩的坑是某些SoC的GPU虽然有完整的驱动但Mesa的“软渲染”路径没有打开或者KMS的Buffer共享方式不兼容导致硬渲染的buffer不能直接被显示控制器读取。表现就是你的Qt界面能跑起来但只有软件渲染CPU占用率一下飙到100%帧率惨不忍睹。这种情况下即使Mesa支持OpenGL效果也等于零。我在选型时需要关注三个关键接口是否完整一是渲染节点支持哪些格式比如ARGB8888、NV12要通过modetest或render_node的信息确认二是EGL是否支持EGL_KHR_platform_gbm这是将GBM创建的buffer关联到EGL上下文的标准方式算是嵌入式Linux平台的基础要件三是KMS的Plane是否支持Scaling/Rotation如果支持那很多2D加速的工作可以直接甩给KMS的硬件图层来做特别适合视频播放的场景。4. 显示服务器之争X11 vs Wayland4.1 X11的历史包袱嵌入式里到底该不该用X11是一个1987年就定型的显示协议它的核心设计思路是“C/S架构”X Server管显示硬件X Client是各种应用程序两者通过X11协议通信。在那个年代这种架构非常先进因为它天然支持跨网络显示把图形显示能力跟本地硬件解耦。但它的代价也很大客户端画完的画面要先通过协议发给ServerServer再合成并提交给硬件显示中间多了一步“运输”环节这可不是免费的。现在的X Server、尤其是X.org实际上已经做了一些优化比如应用可以通过共享内存的DRI3机制直接和GPU交互绕过服务器的数据拷贝。但X11协议层面上那种“为每个基础操作都走Round Trip”的交互方式还是给系统带来了额外的复杂度和延迟。真正让X11显得老态龙钟的是它核心架构中对“合成”这件事没有一个清晰的定位。窗口合成、画面叠加这些事情早期完全交给各窗口自行处理容易出现闪烁和闪烁不协调的问题。在嵌入式Linux项目里X11并非完全不可用很多老产线和工控项目至今还在跑X11稳定性确实久经考验。但如果你在开发新的消费类产品或对交互流畅度有要求的设备我通常不建议把时间花在“踩X11的老坑”上尤其是多窗口透明、旋转、多屏热插拔这些场景X11做起来比Wayland痛苦得多。UE、Qt跑在X11下经常会有莫名其妙的拖影和全屏覆盖失效的问题排查半天最后发现是合成器的锅白白浪费工期。4.2 Wayland的架构为什么更适合现代嵌入式Wayland的设计思路和X11完全相反。它把“合成器”这个职责显式化了Wayland Server也就是合成器比如Weston、Mutter、KWin统一负责所有窗口画面的合成、布局、透明度处理每个应用是Wayland Client它们不直接画到屏幕而是把自己的画面通过共享内存或DMA-BUF提交给合成器。合成器拿到所有窗口的Buffer合成出一整帧画面再通过KMS的Page Flip一次性交给硬件。这套架构最大的好处是每个应用不需要关心其他应用怎么画最终呈现在屏幕上的每帧画都由合成器全权掌控。合成器可以精确地在VBlank时机做合成不会出现X11那种窗口闪烁和半帧撕裂的问题。Wayland对现代图形特性的支持也更自然比如触摸板手势、窗口圆角、模糊背景这些在Wayland里都是合成器的内部操作。对嵌入式来说Wayland还有一个隐藏红利它天然鼓励使用GPU加速合成。因为合成器最终会把所有Buffer合成到一块帧缓冲这个操作如果硬件支持的话可以直接交给GPU的合成器或者显示控制器的多个Plane去做。Weston是目前多见的Wayland参考实现在嵌入式里用得非常多它的layout插件支持桌面板和多屏显示在RK3399这类板子上面跑得特别顺。4.3 嵌入式场景下如何选型按产品需求和团队实力来做嵌入式图形方案选型我一般先问三个问题产品是不是一定要多窗口显示服务器需不需要跟远程桌面联动团队对图形栈的投入产能是多少如果产品是单窗口、全屏的比如一体机、ADAS仪表、工控HMI我一般会强烈建议直接走“无显示服务器”的路线也就是让应用直接通过KMS去显示连Wayland都省掉。Qt有专门的eglfs平台的插件就是干这个的。应用做成全屏画完直接 Flip性能好得让用户怀疑是不是换了块屏幕。很多嵌入式的Qt程序最终都在eglfs下跑。如果要多个窗口同时显示比如需要同时显示地图、视频、控件那Wayland是当前最合适的选择。X11在现代嵌入式设备上唯一值得选的情况是已有庞大X11应用代码库、迁移成本太高的时候。那种情况下做维护要比迁移相对稳妥但新功能开发尽量远离X11特性依赖。关于“带不带显示服务器”项目我再啰嗦一句有人说“裸奔KMS性能最优”这句话方向对但不绝对。KMS下确实没有合成器但你的应用必须把所有合成逻辑自己扛下来。如果不做多窗口叠加裸奔确实既简单又高效但如果有多个缓冲叠加的需求自己写叠加效率可能不如成熟合成器的硬件加速实现。5. 合成器与GUI工具包Weston、GTK、Qt的适配5.1 Weston参考合成器跑通了它等于跑通了WaylandWeston是Wayland官方维护的参考合成器也是最常用的嵌入式Wayland实现。它不仅仅提供一个启动脚本还包含一套插件化的架构核心负责合成和输入插件负责具体的后端策略比如drm-backend.so直接通过KMS显示headless-backend.so用于无屏环境测试。用Weston启动Wayland服务到屏幕简单到出奇但前期的配置和插件顺序容易被忽视。如果你用的是带GPU的板子我推荐用weston.ini里[core]段明确配置backenddrm-backend.so然后[output]段指名显示接口和分辨率避免Weston自己乱探测在一些只有HDMI输出的板子上Weston偶尔会选到一片没有接屏幕的Connector导致你盯着黑屏怀疑人生。注意Weston在DRM后端下的渲染能力取决于Mesa的EGL配置。如果Mesa没配好Weston会自动退回软件渲染启动时在日志里会有 “use pixman renderer” 的提示。这时候你在屏幕上跑什么都慢吞吞的。排查的方式是看启动日志中是否有 “EGL hardware acceleration unavailable” 或者类似字样。没有加速的Weston在嵌入式板上基本不可用。5.2 Qt的嵌入式适配eglfs、linuxfb、wayland平台怎么选Qt对嵌入式Linux的适配特别有意思它提供了一整套“平台插件”机制让你可以决定Qt应用在哪种显示后端上跑。常见的有eglfs直接走EGLDRI显示无显示服务器、linuxfb传统帧缓冲后端适合老内核、wayland跑在Wayland合成器上三种。我帮很多项目调Qt显示后总结了一个粗略的选择原则如果你的系统有一个完整的显示服务器比如Weston而且需要多个应用同时协作选wayland平台插件如果产品界面固定为全屏、应用是自家写的直接选eglfslinuxfb只有在内核还没提供DRM/KMS能力的老板子上才用只要内核版本超过4.x我都劝你别再依赖linuxfb。eglfs跑全屏应用的时候有几个隐藏参数很关键。比如通过环境变量QT_QPA_EGLFS_KMS_CONFIG指定一个JSON配置文件可以设置平台要显示到哪个Connector、旋转角度、分辨率适配甚至指定从哪个DRM master获取显示权限。这些参数在板子一上电就有两个DRM设备比如内置GPU和一个USB DisplayLink设备时会非常有用你要是不指定Qt很可能选到错误的设备上白白黑屏。6. 实操在一块瑞芯微板子上跑通整个图形栈6.1 环境准备与启动方式这一段我用一个比较常见的参考硬件来说明一块RK3566或RK3568核心板带eDP或HDMI屏幕跑一个buildroot或Yocto构建的最小系统。在开始跑图形栈之前先确认内核配置里开了哪些DRM驱动# 查看已加载的DRM显示驱动 ls /sys/class/drm/ # 查看内核drm相关的命令缓存 dmesg | grep drm # 查看GPU节点的存在 ls -l /dev/dri/如果card0和renderD128都存在说明DRM基础是好的。这里的一个技巧是观察dmesg | grep -E rk3568|drm|display的输出能直接看到Panel的识别过程、有没有读取EDID错误、有没有设置分辨率失败。如果有 “failed to find panel” 的提示多半是设备树里LCD屏的时序参数没配好属于系统集成问题不是图形栈问题。6.2 写一个最小的“直接DRM绘画”程序想彻底理解KMS的调用路径最好自己写一个特别小的C程序申请一块DUMB Buffer填成一块纯色或渐变然后做一次ModeSet和PageFlip。我贴出关键几行核心逻辑省略完整错误处理#include xf86drm.h #include xf86drmMode.h int fd open(/dev/dri/card0, O_RDWR); drmModeRes *res drmModeGetResources(fd); // 找第一个连接器 drmModeConnector *conn drmModeGetConnector(fd, res-connectors[0]); drmModeEncoder *enc drmModeGetEncoder(fd, conn-encoder_id); drmModeCrtc *crtc drmModeGetCrtc(fd, enc-crtc_id); // 创建dumb buffer struct drm_mode_create_dumb create {0}; create.width width; create.height height; create.bpp 32; ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, create); // 映射到用户空间往里面画满颜色 struct drm_mode_map_dumb map {0}; map.handle create.handle; ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, map); uint32_t *pixels mmap(0, create.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, map.offset); for (int i 0; i width * height; i) pixels[i] 0xFFFF0000; // 纯红色 // 把buffer设为crtc的scanout buffer完成模式设置 drmModeCrtcSet(fd, crtc-crtc_id, fb_id, 0, 0, conn-connector_id, 1, mode);这个程序虽然是“古董级”做法但能把整个KMS流程完整走一遍对建立底层直觉特别有帮助。第一次在这个环节看到屏幕点亮红色的时候比什么Demo都有成就感。要注意的是新内核推荐走drm_mode_atomic接口即ATOMIC提交但单屏单平面的场景下drmModeCrtcSet这种老接口依然可用而且代码更直观。为了跑通概念先一眼看懂老接口再去翻ATOMIC的例子也不迟。6.3 用weston把Wayland跑起来假设你的板子上已经编译好了 Mesa、libdrm、weston和Qt把Wayland环境跑起来的顺序大概是这样# 设置显示后端为DRM并指定一块FB设备 export WAYLAND_DISPLAYwayland-0 export XDG_RUNTIME_DIR/run/user/0 weston --backenddrm-backend.so --tty1启动之后看日志如果出现wayland-0 is available之类的输出就代表合成器活起来了。这时候再启动一个Qt程序比如一个基于QWidget的示例应用指定QPA为waylandexport QT_QPA_PLATFORMwayland ./your_app如果你看到画面出现在Weston桌面上说明整个链路从Qt到Wayland、到合成器、再到KMS已经全通。我在这里踩过的坑是没设置XDG_RUNTIME_DIRWayland协议要求Unix Socket存放到这个目录里不设置或者目录权限不对应用连合成器都连不上报 “wayland-connection refused” 之类的错第一反应还以为是网口问题排查了半小时才发现是环境变量没配。7. 常见问题与排查技巧实录7.1 黑屏“看一眼就知道大概是什么问题”的经验表我把这几年调图形栈的经验整理成了一张速查表每次遇到黑屏先按这个思路定位。现象可能是哪里第一手检查方法dmesg里有DRM报错但屏幕不亮设备树面板时序或PHY配置错dmesg | grep drm查看是哪个字模块报错应用能跑但屏幕全白/全黑Buffer格式不匹配或没有正确映射modetest -p对比Plane的格式支持列表画面撕裂严重Page Flip没有同步VBlank开了双缓冲没等Fence使用vblank-timestamp或者开启PageFlip全屏模式检查颜色全错红色变蓝RGB/BGR顺序配置错在设备树/内核驱动里检查bus-format配置偶尔花屏或残影Buffer带宽不够或者面板刷新率超出控制器上限降低分辨率/刷新率对比测试Wayland应用连不上XDG_RUNTIME_DIR没设对或目录权限不足检查环境变量和socket权限上面表格里最容易被新手忽略的是“应用能跑但屏幕全白/全黑”。典型场景是Qt的eglfs界面起来了但界面上什么都看不到。这块大多数是分配Buffer时指定的像素格式跟KMS Plane支持的像素格式不一致比如创建了一个ARGB8888的buffer但你的硬件主平面只支持XRGB8888或RGB565最终显示的像素被硬件丢弃或错位。排查方法就是查modetest -p里对每种平面格式的支持情况然后对照Qt或者weston实际选择的格式逐个调整。7.2 屏幕旋转与多屏输出那些坑嵌入式产品里“竖屏”是常态很多一体机、广告机、手持设备都是竖屏布局。在DRM层面做屏幕旋转通常有两种手段一是把旋转角度配置放在显示控制器的硬件模块里让KMS的Plane来做旋转缩放二是靠GPU合成器旋转即大家常见的在OpenGL/EGL里做旋转。前者性能极高但硬件平面的旋转能力各不相同一般只支持0/90/180/270度里的一部分而且会占用额外的Plane资源。实际项目里最稳妥的方案是在设备树里配置面板方向比如RK平台上通过panel-rotation或者rotation属性设置为180让内核在模式设置时直接告诉控制器做旋转。应用层代码完全不需要感知方向任何QWidget/GTK程序都能正常工作。这张表里我特别想提的是屏幕方向变了但触摸屏没变点按坐标就对不上了很多产品就是这么“看起来显示正常但一戳就露馅”。触摸和显示的坐标系必须同时转这几乎是竖屏项目里百分之百会遇到的坑。多屏输出是嵌入式里另一类难题。RK3568这类SoC一般支持两个HDMI输出或者一HDMI一MIPI-DSI。Weston做多屏时要注意[output]段的配置不同输出接口的优先级要人工指定。如果让合成器自己去探测常出现的现象是一个屏幕有桌面另一个屏幕黑着日志却完全没有报错。这需要检查每个Connector连接状态不要只看dmesg用modetest -M 你的驱动 -c明确看每个Connector的connected属性。7.3 性能调优为什么我的UI卡成PPT图形栈性能问题的根源绝大多数出在“某个环节走了软件路径没有发挥硬件加速”。我遇到最典型的情况是Mesa里的EGL配置默认没打开DRI3/DMA-BUF导致weston和Qt全程用pixman软件渲染或者内核对某块buffer的物理连续内存分配失败Mesa被迫回退到不连续内存导致显示控制器读取时出错卡顿。性能定位的方法我一般分三步。第一步看/sys/kernel/debug/dri/0/state内容确认当前是否走的是硬件加速的Plane、Buffer物理地址是否连续第二步看GPU占用率用perf或GPU厂商的调试工具Arm有mali_profiler观察渲染和合成各占多少第三步直接开带FPS统计的Qt应用测试逐步关掉特效区分是GPU性能瓶颈还是KMS调度瓶颈。还有一个容易翻车的盲点CPU调频策略。好几个项目上图形性能差最后查到原因是SoC的GPU和CPU跑在非常保守的低频档因为内核里选的变频策略太保守而并不是图形栈本身有瓶颈。这种情况下换性能优化策略或者干脆设置固定的最高频率帧率立竿见影地翻倍。注意频率调高会带来发热产品量产时要综合考虑功耗和散热。8. 我的一些实际体会做嵌入式Linux图形这块久了最大的感受是它不像纯网络、纯驱动那样有清晰的边界它夹在驱动、内核、用户态库、应用框架之间任何一个环节的“微小偏差”都会被放大成黑屏或者卡顿。很多问题不是你不会写代码而是你对这一整条链路缺一幅完整的地图。我自己常用的办法是拿到一块新板子花一到两天时间把整条链路的最小实例全部跑通先跑modetest点亮屏幕再跑kmscube渲染一个旋转方块再启动weston最后把Qt应用放进去。每多跑通一层都会把这一层跟上下层的接口关系记下来。这样后面不管产品出什么问题心里都有一个“大概在哪里出事了”的地图排查效率能快一倍。希望这篇分享也能帮你画出自己的那张图。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑