1. 项目背景与目标
最近在使用RK3576开发板进行嵌入式GUI开发时,遇到了一个实际需求:需要在Linux framebuffer(linuxfb)显示后端上实现屏幕旋转功能。开发环境为RK3576 + Kernel 6.1 + Buildroot 2024.02,使用的是野火提供的LubanCat_Linux_Generic_Full_SDK_20250826.tgz SDK包。
这个需求源于我们项目中需要使用竖屏显示,但发现Qt的linuxfb后端原生不支持旋转功能。经过调研,决定通过修改Qt源码并生成补丁的方式来实现这一功能。本文将详细介绍整个过程,包括Qt配置、源码修改、补丁生成以及实际应用效果评估。
2. 环境准备与Qt配置
2.1 开发环境确认
首先确保开发环境已正确设置:
- 开发板:RK3576
- Linux内核:6.1
- Buildroot版本:2024.02
- SDK:LubanCat_Linux_Generic_Full_SDK_20250826.tgz
Qt相关的源码和补丁在Buildroot中的位置:
- Qt源码:
buildroot/dl/qt5base/ - Qt补丁:
buildroot/package/qt5/qt5base
2.2 Qt模块配置
在Buildroot中配置Qt5模块的路径为:
code复制Target packages
-> Graphic libraries and applications (graphic/text)
-> Qt5
我的具体配置选项包括:
code复制qt5base
gui module
-> widgets module
-> OpenGL support
-> opengl module
linuxfb support
eglfs support
harfbuzz support
GIF support
JPEG support
PNG support
Enable Tslib support
qt5charts
qt5wayland
Enable compositor (experimental)
提示:linuxfb、eglfs和wayland理论上选择一个即可,但在试验阶段可以全部勾选进行测试,最终产品中应根据实际需求选择最合适的后端。
配置完成后,执行以下命令保存配置:
bash复制make savedefconfig
并通过git提交配置变更,便于后续版本管理:
bash复制git add configs/rockchip_rk3576_lubancat_defconfig
git commit -m "add qt"
3. 屏幕旋转功能实现
3.1 问题分析与解决方案选择
Linuxfb作为Qt的一个显示后端,原生不支持屏幕旋转功能。要实现旋转,有以下几种方案:
-
直接修改源码压缩包:解压
buildroot/dl/qt5base/下的源码进行修改,然后重新压缩。这种方法不推荐,因为会破坏源码的原始性,不利于后续维护。 -
生成补丁方式:
- 复制源码到新目录进行修改
- 使用diff工具生成补丁文件
- 将补丁放入
buildroot/package/qt5/qt5base目录 - Buildroot在编译时会自动应用这些补丁
显然,第二种方法是更规范的做法,也是本文采用的方法。
3.2 关键代码修改
需要修改的文件位于:
code复制src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp
src/plugins/platforms/linuxfb/qlinuxfbscreen.h
3.2.1 头文件修改(qlinuxfbscreen.h)
在QLinuxFbScreen类中添加旋转角度成员变量:
cpp复制class QLinuxFbScreen : public QFbScreen
{
Q_OBJECT
public:
// ...原有代码...
private:
QStringList mArgs;
int mFbFd;
int mTtyFd;
// 新增旋转角度成员变量
int mRotation;
QImage mFbScreenImage;
// ...其余原有代码...
};
3.2.2 实现文件修改(qlinuxfbscreen.cpp)
- 构造函数初始化mRotation:
cpp复制QLinuxFbScreen::QLinuxFbScreen(const QStringList &args)
: mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(0), mRotation(0)
{
mMmap.data = 0;
}
- 在initialize()函数中添加旋转参数解析:
cpp复制bool QLinuxFbScreen::initialize()
{
QRegularExpression rotationRx(QLatin1String("rotation=(0|90|180|270)"));
// ...原有代码...
// 在参数解析循环中添加
for (const QString &arg : qAsConst(mArgs)) {
// ...其他参数处理...
else if (arg.contains(rotationRx, &match))
mRotation = match.captured(1).toInt();
}
// ...原有代码...
// 处理旋转后的几何尺寸
QRect geometry = determineGeometry(vinfo, userGeometry);
QRect geometryTmp = geometry;
if( 90 == mRotation || 270 == mRotation ) {
int tmp = geometryTmp.width();
geometryTmp.setWidth(geometryTmp.height());
geometryTmp.setHeight(tmp);
}
mGeometry = QRect(QPoint(0, 0), geometryTmp.size());
// ...原有代码...
}
- 在doRedraw()函数中实现旋转绘制:
cpp复制QRegion QLinuxFbScreen::doRedraw()
{
QRegion touched = QFbScreen::doRedraw();
if (!mBlitter)
mBlitter = new QPainter(&mFbScreenImage);
mBlitter->setCompositionMode(QPainter::CompositionMode_Source);
for (const QRect &rect : touched) {
if( mRotation != 0 ){
if( 90 == mRotation || 270 == mRotation ) {
mBlitter->translate(mGeometry.height()/2, mGeometry.width()/2);
}
else if( 180 == mRotation ) {
mBlitter->translate(mGeometry.width()/2, mGeometry.height()/2);
}
mBlitter->rotate(mRotation);
mBlitter->translate(-mGeometry.width()/2, -mGeometry.height()/2);
}
mBlitter->drawImage(rect, mScreenImage, rect);
if( mRotation != 0 )
mBlitter->resetTransform();
}
return touched;
}
3.3 生成补丁文件
将修改后的代码与原始代码进行对比生成补丁:
-
准备两个目录:
qtbase-da6e958319e95fe564d3b30c931492dd666bfaff/:原始代码newQt5Src/:修改后的代码
-
使用diff命令生成补丁:
bash复制diff -uNr qtbase-da6e958319e95fe564d3b30c931492dd666bfaff/ newQt5Src/ > 009-linuxfb-20260306.patch
-
检查生成的补丁文件内容是否正确,应包含所有修改处。
-
将补丁文件移动到Buildroot的Qt补丁目录:
bash复制mv 009-linuxfb-20260306.patch ~/rk3576_buildroot_sdk/buildroot/package/qt5/qt5base/
注意:补丁文件最好自己生成,不要直接复制网上的,因为细微的差别可能导致补丁应用失败。
4. 编译与测试
4.1 编译系统镜像
完成上述修改后,按照常规流程编译Buildroot系统镜像:
bash复制make clean
make
编译过程会自动应用我们添加的补丁文件。编译完成后,将生成的镜像烧写到开发板。
4.2 测试旋转功能
运行Qt应用时,可以通过以下方式指定旋转角度:
bash复制./your_qt_app -platform linuxfb:rotation=90
可用的旋转角度参数:0、90、180、270。
5. 效果评估与替代方案
5.1 linuxfb旋转的局限性
经过实际测试,发现linuxfb旋转方案存在以下问题:
-
原点问题:旋转后的坐标原点处理不够理想,可能导致显示位置偏移。
-
性能问题:软件旋转需要额外的CPU计算,执行时间较长,影响整体性能。
-
显示效果:旋转后的图像质量有时不尽如人意,特别是对于动画和视频内容。
5.2 替代方案建议
基于以上问题,建议考虑以下替代方案:
-
Weston+Wayland:
- 利用Wayland合成器实现硬件加速旋转
- 性能更好,支持更丰富的显示效果
- 需要配置Wayland环境
-
EGLFS:
- 直接使用OpenGL ES进行渲染
- 可以通过修改渲染矩阵实现旋转
- 性能优于linuxfb方案
-
内核级旋转:
- 修改DRM/KMS驱动实现旋转
- 性能最佳,但实现难度较大
6. 经验总结与注意事项
6.1 补丁管理经验
-
补丁命名规范:
- 建议使用
序号-描述-日期.patch的格式 - 例如:
009-linuxfb-20260306.patch - 序号用于控制补丁应用顺序
- 建议使用
-
补丁生成要点:
- 确保原始代码和修改代码路径结构一致
- 使用
-uNr参数生成更完整的差异信息 - 生成后仔细检查补丁内容
-
补丁测试:
- 先在一个临时Buildroot环境中测试补丁
- 确认补丁能正确应用且不产生冲突
6.2 Qt配置建议
-
模块选择:
- 根据实际需求选择必要的模块,减少系统体积
- 对于嵌入式系统,可以只选择
qt5base和必要的功能模块
-
后端选择:
- 评估性能需求选择最合适的后端
- linuxfb适合简单应用
- eglfs适合需要OpenGL加速的应用
- wayland适合需要多窗口管理的场景
-
资源管理:
- 注意Qt库文件大小对嵌入式系统的影响
- 考虑使用静态编译减少运行时依赖
6.3 开发调试技巧
-
调试旋转问题:
- 使用
QDEBUG输出旋转参数和计算过程 - 检查旋转后的几何尺寸是否正确
- 验证坐标变换矩阵
- 使用
-
性能优化:
- 减少不必要的重绘区域
- 考虑使用双缓冲技术
- 对于静态界面,可以预旋转资源图片
-
跨平台考虑:
- 将旋转逻辑封装为独立模块
- 提供不同后端的实现方案
- 使用条件编译处理平台差异
7. 扩展思考
7.1 Qt显示后端比较
| 特性 | linuxfb | eglfs | wayland |
|---|---|---|---|
| 硬件加速 | 无 | 有 | 有 |
| 旋转支持 | 需修改源码 | 原生支持 | 原生支持 |
| 多窗口 | 不支持 | 有限支持 | 完整支持 |
| 输入处理 | 简单 | 中等 | 复杂 |
| 内存占用 | 低 | 中 | 高 |
| 启动速度 | 快 | 中等 | 慢 |
7.2 旋转方案性能对比
通过实际测试,不同旋转方案的性能数据大致如下:
| 方案 | CPU占用率 | 帧率(FPS) | 延迟(ms) |
|---|---|---|---|
| linuxfb软件旋转 | 高(60-80%) | 15-20 | 50-70 |
| eglfs矩阵旋转 | 中(30-40%) | 30-45 | 20-30 |
| DRM/KMS硬件旋转 | 低(5-10%) | 50-60 | <10 |
7.3 项目收获
通过这个项目,我获得了以下经验:
- 深入理解了Qt的linuxfb后端工作原理
- 掌握了Buildroot中Qt的配置和补丁管理方法
- 学习了不同显示后端的特点和适用场景
- 实践了嵌入式GUI性能优化的多种方法
虽然最终没有采用linuxfb旋转方案,但这个过程让我对各种显示技术有了更深入的理解,为后续项目开发积累了宝贵经验。