0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

double buffer的fbdev驱动与应用

FPGA之家 来源:CSDN技术社区 作者:嵌入式Hacker 2021-03-21 11:26 次阅读

一、为何需要 double buffer?

single buffer 会导致:

屏幕撕裂(tearing),即在屏幕上同时看到多帧数据拼接在一起。

点击查看大图

single buffer 为何会造成撕裂:

refresh rate 和 frame rate 不一致。

refresh rate 表示的是 屏幕每秒能更新多少次显示,例如 30hz / 60hz。

点击查看大图

frame rate 表示的是 lcd controller / gpu 每秒能绘制多少帧数据,例如 30fps / 60fps。

点击查看大图

LCD controller / gpu 和 屏幕协作完成一帧图像的显示:

点击查看大图

在 single buffer 的场景下,LCD user 和 LCD controller / gpu 总是在共用同一个 framebuffer,且没有同步机制。

LCD user 是写者,LCD controller / gpu 是读者。

由于存在竞争关系且读写没有同步机制,framebuffer 里必须会发生同时存在frame N 和 frame N-1 的数据,此时 LCD 将 framebuffer 的数据显示出来时,就会看到撕裂的效果:

点击查看大图

可以通过 double buffer+vsync 解决撕裂的问题。

double buffer,顾名思义,就是有 2 个 framebuffer,其工作逻辑如下:

LCD controller : draw fb0 to screen

LCD user : write data to fb1

LCD controller : draw fb1 to screen

LCD user : write data to fb0

循环。..

vsync 机制则用于确保一帧图像能不被打断地显示在屏幕。

如何支持 double buffer?

需要驱动和应用互相配合:

二、编写支持 double buffer 的fbdev 驱动

fbdev 框图:

先梳理一下思路:

让驱动支持 double buffer 需要做 3 件事。

1. 申请2 x buffer:

size = (2 * width * height);

fbi-》screen_base = dma_alloc_wc(sfb-》dev, size, &map_dma, GFP_KERNEL);

2. 将 buffer 相关的信息保存 struct fb_info-》 struct fb_var_screeninfo。

struct fb_var_screeninfo {

__u32 xres; /* visible resolution */

__u32 yres;

__u32 xres_virtual; /* virtual resolution */

__u32 yres_virtual;

__u32 xoffset; /* offset from virtual to visible */

__u32 yoffset; /* resolution */

。..

}

点击查看大图

xres 和 yres 是真实的 LCD 分辨率的宽和长;

xres_virtual 和 yres_virtual 是显存区域的宽和长;

xoffset 和 yoffset 用于指定当前使用哪一个 Buffer 进行绘制。使用 Buffer0 时 ,xoffset = 0,yoffset=0; 使用 Buffer1 时,xoffset = 0, yoffset = yres * 1;

3. 支持切换 buffer,具体的就是实现 ioctl:FBIOPAN_DISPLAY。

pan 的本意是平移,可以想象成显存上方有一个取景框,平移取景框可以看到不同的显示内容。

实例分析:goldfishfb.c

goldfishfb.c 是虚拟硬件 goldfish 的 fbdev 驱动,我们可以参考这个文件,学习如何实现 double buffer。

1. 分配 2 x buffer:

int goldfish_fb_probe()

{

。..

framesize = width * height * 2 * 2;

fb-》fb.screen_base = (char __force __iomem *)dma_alloc_coherent(&pdev-》dev, framesize, &fbpaddr, GFP_KERNEL);

}

2. 设置 fb_var_screeninfo:

int goldfish_fb_probe()

{

。..

fb-》fb.var.xres = width;

fb-》fb.var.yres = height;

fb-》fb.var.xres_virtual = width;

fb-》fb.var.yres_virtual = height * 2;

}

3. 实现 ioctl / FBIOPAN_DISPLAY:

static struct fb_ops goldfish_fb_ops = {

。..

.fb_pan_display = goldfish_fb_pan_display,

};

int goldfish_fb_pan_display()

{

。..

// 将新的显存地址告知 lcd controller

writel(fb-》fb.fix.smem_start + fb-》fb.var.xres * 2 * var-》yoffset,

fb-》reg_base + FB_SET_BASE);

// 等待 LCD controller 的 vsync 信号

wait_event_timeout(fb-》wait,fb-》base_update_count != base_update_count, HZ / 15);

}

当LCD controller 将一帧图像完整地显示在 LCD 上后,就会产生一个中断,在中断里就会执行唤醒睡眠在 fb_pan_display 里的进程。

如果你想多了解一些,可以阅读 DRM 框架里的 fbdev 兼容代码,此代码也是支持 double buffer的:

linux/drivers/gpu/drm/*/*_drm_fbdev.c

linux/drivers/gpu/drm/drm_fb_helper.c

三、编写支持 double buffer 的 fbdev 应用

驱动支持 double buffer 后,还得在应用程序里将其使用起来。

先梳理一下思路:

检查是否支持 double buffer;

使能 double buffer:FBIOPUT_VSCREENINFO;

更新 buffer 里数据;

通知驱动切换 buffer:FBIOPAN_DISPLAY;

等待切换完成:FBIO_WAITFORVSYNC;

实例分析:show_color.c

static int fd_fb;

static struct fb_fix_screeninfo fix; /* Current fix */

static struct fb_var_screeninfo var; /* Current var */

static int screen_size;

static unsigned char *fb_base;

static unsigned int line_width;

static unsigned int pixel_width;

int main(int argc, char **argv)

{

int i;

int ret;

int buffer_num;

int buf_idx = 1;

char *buf_next;

unsigned int colors[] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0, 0x00FFFFFF}; /* 0x00RRGGBB */

struct timespec time;

。..

fd_fb = open(“/dev/fb0”, O_RDWR);

ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);

ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);

line_width = var.xres * var.bits_per_pixel / 8;

pixel_width = var.bits_per_pixel / 8;

screen_size = var.xres * var.yres * var.bits_per_pixel / 8;

// 1. 获得 buffer 个数

buffer_num = fix.smem_len / screen_size;

printf(“buffer_num = %d

”, buffer_num);

fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);

if (fb_base == (unsigned char *)-1) {

printf(“can‘t mmap

”);

return -1;

}

if ((argv[1][0] == ’s‘) || (buffer_num == 1)) {

printf(“single buffer:

”);

while (1) {

for (i = 0; i 《 sizeof(colors)/sizeof(colors[0]); i++) {

lcd_draw_screen(fb_base, colors[i]);

nanosleep(&time, NULL);

}

}

} else {

printf(“double buffer:

”);

// 2. 使能多 buffer

var.yres_virtual = buffer_num * var.yres;

ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);

while (1) {

for (i = 0; i 《 sizeof(colors)/sizeof(colors[0]); i++) {

// 3. 更新 buffer 里的数据

buf_next = fb_base + buf_idx * screen_size;

lcd_draw_screen(buf_next, colors[i]);

// 4. 通知驱动切换 buffer

var.yoffset = buf_idx * var.yres;

ret = ioctl(fd_fb, FBIOPAN_DISPLAY, &var);

if (ret 《 0) {

perror(“ioctl() / FBIOPAN_DISPLAY”);

}

// 5. 等待帧同步完成

ret = 0;

ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);

if (ret 《 0) {

perror(“ioctl() / FBIO_WAITFORVSYNC”);

}

buf_idx = !buf_idx;

nanosleep(&time, NULL);

}

}

}

munmap(fb_base , screen_size);

close(fd_fb);

return 0;

}

运行:

$ 。/show_color single

buffer_num = 1

single buffer:

$ 。/show_color double

buffer_num = 2

double buffer:

该程序会在屏幕上循环的显示不同的颜色。

当传入 “single” 参数时,使用单 buffer,可见撕裂。

当传入 “double” 参数时,使用双 buffer,不再撕裂。

代码不是很复杂,我就不再详细分析了。

如果你想多了解一些,可以阅读开源软件 SDL-1.2 里的 sdl_fbvideo.c,此代码也支持了 double buffer。
编辑:lyn

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 驱动
    +关注

    关注

    12

    文章

    1788

    浏览量

    84896
  • 编辑
    +关注

    关注

    0

    文章

    28

    浏览量

    11530

原文标题:Linux 驱动开发 / fbdev 双缓存 / 快速入门

文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    STM32的DAC输出有个BUFFER功能,BUFFer驱动能力究竟有多大,普通的运放输出可以吗?驱动LED可以吗?

    DAC是输出模拟电压给外界参考,STM32的DAC输出有个BUFFER功能,但不知这个BUFFer驱动能力究竟有多大,普通的运放输出可以吗?驱动LED可以吗?有没有人做过这块,给个建
    发表于 05-15 07:42

    OpenHarmony语言基础类库【@ohos.buffer (Buffer)】

    Buffer对象用于表示固定长度的字节序列,是专门存放二进制数据的缓存区。
    的头像 发表于 04-23 17:34 733次阅读
    OpenHarmony语言基础类库【@ohos.<b class='flag-5'>buffer</b> (<b class='flag-5'>Buffer</b>)】

    请问基于驱动器模式=High-Z、Input buffer=禁用设置的GPIO连接是什么?

    我正在使用 TRAVEO CYT2B9,我在 Architecture TRM 中看到了 GPIO 单元架构。 我想知道 Drive mode=High-Z、Input buffer=禁用
    发表于 01-26 06:26

    时钟Buffer芯片是什么?其作用是啥?它被用在什么地方?

    时钟Buffer芯片是什么?其作用是啥?它被用在什么地方? 时钟Buffer芯片是一种用于管理和增强电子设备中的时钟信号的集成电路。时钟信号在数字电子设备中非常重要,它用于同步各个模块的工作,确保
    的头像 发表于 01-16 15:10 3201次阅读

    想实现unsigned char和double的转换,为什么DSP仿真一直不对?

    请教数据类型转换问题,处理器类型:ADSP21479,编译器visual DSP 5.0。 想实现unsigned char和double的转换(因为从外部接口接收到的是unsigned char
    发表于 01-11 08:29

    buffer和cache的区别

    buffer和cache的区别 缓冲区(Buffer)和缓存(Cache)是计算机系统中用于提高数据读写效率的两个关键概念,它们虽然功能有所重叠,但在实际应用中存在一些差异。在下文中,将详尽、详实
    的头像 发表于 12-07 11:00 681次阅读

    简单聊一聊DPT技术-double pattern technology

    今天想来简单聊一聊DPT技术-double pattern technology,也就是双层掩模版技术,在目前先进工艺下,这项技术已经应用的很普遍了。
    的头像 发表于 12-05 14:26 1024次阅读

    什么是always on buffer?什么情况下需要插always on buffer

    相比普通的buffer cell,always on buffer(AOB)有secondary always on pin,可以让AOB即使在primary power off的情况下保持on的状态;AOB在secondary pg pin off的情况下也是off的。
    的头像 发表于 12-01 15:31 1883次阅读
    什么是always on <b class='flag-5'>buffer</b>?什么情况下需要插always on <b class='flag-5'>buffer</b>?

    Double 4 VR智能互动系统

    随着科技的不断发展,教育领域也在不断地进行创新和改革。其中,VR(虚拟现实)技术的引入,为轨道交通实训教学带来了全新的可能性。**Double 4 VR智能互动系统基于多项前沿技术,为轨道交通实训
    的头像 发表于 11-21 10:33 347次阅读

    一网打尽总结 Mysql的所有Buffer

    所以 innodb 自己维护了一个 buffer pool,在读取数据的时候,会把数据加载到缓冲池中,这样下次再获取就不需要从磁盘读了,直接访问内存中的 buffer pool 即可。
    的头像 发表于 11-10 16:08 425次阅读
    一网打尽总结 Mysql的所有<b class='flag-5'>Buffer</b>

    HMC940: 13 Gbps, 1:4 Fanout Buffer w/ Programmable Output Voltage Data Sheet HMC940: 13 Gbps, 1:4 Fanout Buffer w/ Programmable Output Volta

    电子发烧友网为你提供ADI(ADI)HMC940: 13 Gbps, 1:4 Fanout Buffer w/ Programmable Output Voltage Data Sheet相关产品
    发表于 10-13 18:33
    HMC940: 13 Gbps, 1:4 Fanout <b class='flag-5'>Buffer</b> w/ Programmable Output Voltage Data Sheet HMC940: 13 Gbps, 1:4 Fanout <b class='flag-5'>Buffer</b> w/ Programmable Output Volta

    AD8155: Xstream™ 6.5 Gbps Dual Buffer Mux/Demux Data Sheet AD8155: Xstream™ 6.5 Gbps Dual Buffer Mux/Demux Data Sheet

    电子发烧友网为你提供ADI(ADI)AD8155: Xstream™ 6.5 Gbps Dual Buffer Mux/Demux Data Sheet相关产品参数、数据手册,更有AD8155
    发表于 10-12 18:52
    AD8155: Xstream™ 6.5 Gbps Dual <b class='flag-5'>Buffer</b> Mux/Demux Data Sheet AD8155: Xstream™ 6.5 Gbps Dual <b class='flag-5'>Buffer</b> Mux/Demux Data Sheet

    HMC560A: 22 GHz to 38 GHz, GaAs, MMIC, Double Balanced Mixer Data Sheet HMC560A: 22 GHz to 38 GHz, GaAs, MMIC, Double Balanced Mixer Data Sh

    电子发烧友网为你提供ADI(ADI)HMC560A: 22 GHz to 38 GHz, GaAs, MMIC, Double Balanced Mixer Data Sheet相关产品参数、数据
    发表于 10-12 18:49
    HMC560A: 22 GHz to 38 GHz, GaAs, MMIC, <b class='flag-5'>Double</b> Balanced Mixer Data Sheet HMC560A: 22 GHz to 38 GHz, GaAs, MMIC, <b class='flag-5'>Double</b> Balanced Mixer Data Sh

    float与double计算时间差多少?

    float与double计算时间差多少
    发表于 10-11 07:31

    在单片机里double占用几个字节?

    在单片机里double占用几个字节
    发表于 09-26 08:02