drm学习笔记5-撕裂现象和double-buffer

本文详细解释了Android中单buffer的撕裂现象,并通过代码示例展示了如何通过双buffer技术来避免。通过对比单buffer和双buffer的工作原理,演示了如何在main函数中使用两个framebuffer,以确保流畅的视频播放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

撕裂现象

上一篇介绍了单buffer用例,在实际应用至少都是双buffer。2012年android提出了黄油计划采用了3 buffer,使得android系统性能在流畅度方面得到了很大提升。更有国内某手机在播放高帧率视频时更是采用了4 buffer方案。
单buffer最明显的问题就是tear effect(撕裂现象)。这里通过代码演示下撕裂现象,进而引出双buffer用例。
本文代码是在modeset-single-buffer基础上进行了修改。
单buffer撕裂产生 的原因:

单buffer数据在前台显示时,由于随后的画面数据需要再次使用该buffer时由于耗时没有及时填充完,而导致下一帧显示时有部分数据是前一帧的。

演示思路:

有2帧画面,每隔1s会显示一帧画面。第一帧灰色,第二帧白色。第二帧耗时2s,而在第2s的时候只能填充第二帧的一半数据,导致有一半是灰色,一半是白色。

根据modeset_create_fb函数修改,新增代码:

static int vsync_frame(int fd, struct buffer_object *bo, int frame)
{
	struct drm_mode_create_dumb create = {};
 	struct drm_mode_map_dumb map = {};

	create.width = bo->width;
	create.height = bo->height;
	create.bpp = 32;
	drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);

	bo->pitch = create.pitch;
	bo->size = create.size;
	bo->handle = create.handle;
	drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
			   bo->handle, &bo->fb_id);

	map.handle = create.handle;
	drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

	bo->vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,
			MAP_SHARED, fd, map.offset);
	//帧率填充不同数据
	if(frame == 1) {
		memset(bo->vaddr, 0x80, bo->size);
	} else if (frame == 2) {
		memset(bo->vaddr, 0xcc, bo->size/2);
		memset(bo->vaddr + bo->size/2, 0x80, bo->size/2);
	} else if (frame == 3) {
		memset(bo->vaddr,  0xcc, bo->size);
	}
	return 0;
}
//main函数修改:
int main(int argc, char **argv)
{
...
	buf.height = conn->modes[0].vdisplay;

	// modeset_create_fb(fd, &buf);
	int frame = 1;
	while (frame < 4) {
		vsync_frame(fd, &buf, frame);
		drmModeSetCrtc(fd, crtc_id, buf.fb_id,
			0, 0, &conn_id, 1, &conn->modes[0]);
		frame ++;
		sleep(1);
	}
	
	getchar();
...
}

实现效果如下:

在这里插入图片描述

双buffer

由于单buffer存在撕裂现象。所以将新帧画面绘制到framebuffer时,需要绘制到一个新buffer,而不是front-buffer(即正在使用的buffer),避免这种现象发生的技术称为double-buffering。双bufferd:front-buffer用于前台显示,back-buffer用于后台绘制。当一帧渲染完成时,进行交换2个buffer(即swapbuffer)。交换并不是指复制数据,而是只交换buffer指针。在绘制到front-buffer之前,我们需要找到back-buffer。每个buffer由宽度、高度、步幅、大小、句柄、地图和 fb-id 组成。

参考何小龙double代码进行修改:

static int vsync_frame(int fd, struct buffer_object *bo, int color)
{
...
	memset(bo->vaddr,  color, bo->size);

	return 0;
}

int main(int argc, char **argv)
{
	...
	bufs[0].width = conn->modes[0].hdisplay;
	bufs[0].height = conn->modes[0].vdisplay;
	bufs[1].width = conn->modes[0].hdisplay;
	bufs[1].height = conn->modes[0].vdisplay;
	vsync_frame(fd, &bufs[0], 0x80);
	drmModeSetCrtc(fd, crtc_id, bufs[0].fb_id,
			0, 0, &conn_id, 1, &conn->modes[0]);
	sleep(1);
	vsync_frame(fd, &bufs[1], 0xcc);
	drmModeSetCrtc(fd, crtc_id, bufs[1].fb_id,
			0, 0, &conn_id, 1, &conn->modes[0]);
	sleep(1);
	// modeset_create_fb(fd, &buf);
	getchar();
	modeset_destroy_fb(fd, &bufs[0]);
	modeset_destroy_fb(fd, &bufs[1]);
...
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜卓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值