创龙TL113开发板中,在屏幕刷新较快的页面中,存在页面撕裂的问题。通过修改QT库的代码已经能解决,效率稍微有损耗。你是怎么解决的呢,欢迎交流。
离线
Qt 5 的 linuxfb 插件默认情况下通常使用的是单缓冲(Single Buffering)机制,或者更准确地说是直接渲染(Direct Rendering)到帧缓冲区的模式。
这意味着 Qt 的绘图指令会直接写入到当前正在显示的显存区域中。如果不采取额外措施,这会导致以下问题:
画面撕裂(Screen Tearing):当画面快速移动或动画刷新时,屏幕上半部分可能显示旧帧,下半部分显示新帧。
闪烁(Flickering):如果绘图操作耗时较长,用户可能会看到绘图过程中的中间状态(例如先看到背景色,再看到按钮)。
以下是关于 Qt 5 linuxfb 缓冲机制的详细解析及解决方案:
1. 为什么默认是单缓冲?
linuxfb 插件的设计初衷是简单和通用。
直接操作显存:它通过 mmap 将 /dev/fb0 映射到内存,然后直接将像素数据写入该地址。
缺乏硬件翻转支持:标准的 linuxfb 插件并不总是自动检测或启用底层硬件的“页翻转”(Page Flipping)功能。除非底层驱动和 Qt 配置完美配合,否则它只是简单地把数据“涂”在屏幕上。
资源限制:在嵌入式系统中,显存(VRAM)通常非常有限。双缓冲需要占用双倍显存,因此默认不开启以节省资源。
2. 如何实现双缓冲?
既然默认是单缓冲,你需要通过以下几种方式来开启双缓冲,以解决撕裂问题:
方案 A:手动调整 Framebuffer 虚拟分辨率(底层方法)
如果你必须使用纯 Framebuffer 模式(不支持 DRM),你需要确保底层显存足够大,能容纳两帧图像,并让 Qt 知道这一点。
修改内核/驱动:确保 Framebuffer 驱动的 yres_virtual(虚拟垂直分辨率)至少是 yres(物理分辨率)的 2 倍。
应用层调整:在 Qt 启动前,通过代码或工具(如 fbset)设置虚拟分辨率。
// 伪代码:在 main() 最开始调用
struct fb_var_screeninfo vinfo;
ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
vinfo.yres_virtual = vinfo.yres * 2; // 分配双倍高度显存
ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo);注意:即使显存足够大,Qt 的 linuxfb 插件在某些旧版本中可能仍然不会自动进行页翻转,而是只使用第一部分显存。
流程如下:
获取当前屏幕信息:使用 FBIOGET_VSCREENINFO 获取 var 结构体。
修改偏移量:改变 var.yoffset 的值。
发起切换:使用 FBIOPAN_DISPLAY 发送指令。
等待垂直同步(可选但推荐):使用 FBIO_WAITFORVSYNC 等待屏幕刷新完成,防止画面撕裂。
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int switch_buffer(int fb_fd, int buffer_index, int screen_height)
{
struct fb_var_screeninfo var;
// 1. 获取当前的屏幕变量信息
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &var) == -1) {
perror("Error reading var info");
return -1;
}
// 2. 计算新的 yoffset
// 如果 buffer_index 是 0,offset 为 0;如果是 1,offset 为屏幕高度
var.yoffset = buffer_index * screen_height;
// 3. 发起页面翻转请求
if (ioctl(fb_fd, FBIOPAN_DISPLAY, &var) == -1) {
perror("Error panning display");
return -1;
}
printf("Switched to buffer %d\n", buffer_index);
return 0;
}
// 使用示例
int main() {
int fb_fd = open("/dev/fb0", O_RDWR);
// ... 初始化 mmap 等 ...
// 假设屏幕高度为 480
// 切换到 Buffer 1 (yoffset = 480)
switch_buffer(fb_fd, 1, 480);
// ... 绘制下一帧 ...
// 切换回 Buffer 0 (yoffset = 0)
switch_buffer(fb_fd, 0, 480);
close(fb_fd);
return 0;
}方案 B:应用层绘图优化(QPixmap)
如果你无法修改底层驱动,可以在代码层面实现“软双缓冲”。
原理:不要直接在 QWidget 或 QWindow 上绘制。先在一个 QPixmap(内存中的位图)上绘制所有图形,绘制完成后,使用 painter.drawImage() 或 painter.drawPixmap() 将这个完整的图像一次性“贴”到屏幕上。
优点:完全由应用程序控制,不依赖驱动特性。
缺点:涉及内存拷贝,如果分辨率很高(如 1080P),可能会增加 CPU 负载。
离线