android多媒体框架介绍(三)显示图形系统之framebuffer

FrameBuffer是linux提供的显存驱动,在android环境设备节点是/dev/graphics/fb*(支持多个屏幕显示,第一个fb0是主显示屏幕,在linux下一般是/dev/fb*)。FrameBuffer的目的就是通过对硬件的封装抽象,让上层通过设备节点文件的方式,操作硬件去显示某个内存的内容。

一、framebuffer模块定义与规格场景分析

我们先明确一点,fb是一个和硬件逻辑强耦合的硬件驱动模块,它就是图形层的逻辑抽象驱动。

模块定义:fb的本质就是把 指定内存 的 内容显示到屏上

例如下图,两块显存,显存1是一个需要全屏显示的图片,它的内存格式RGB565 分辨率720p 走 fb0驱动,显存2是一个鼠标,格式是ARGB8888 分辨率64x64 走fb1驱动,显示设备是一个FHD分辨率的RGB888设备。
这个过程一个典型的逻辑处理距离举例:fb0对应的逻辑图层 读取显存1的720p的RGB565数据 缩放到 FHD然后做格式转换 叠加到设备 显示通路上,fb1的逻辑图层读取 显存2的64x64的数据,转换到显示设备 叠加到对应的坐标上(层之间存在Zorder叠加顺序,鼠标层在上)。
在这里插入图片描述

framebuffer常见的设计规格

fb常见的典型规格:支持几个图形层(如果只有一个图层,那么鼠标必须叠加到内容中),每个图形层 支持哪些 像素数据格式 / 分辨率 / 颜色空间 / 是否支持裁剪crop / 是否支持显示偏移设置 / 是否支持压缩数据解压读取(降低带宽) / 是否支持缩放 等;

二、framebuffer主要流程与原理分析

一个典型的fb访问流程如下:

1、打开/dev/fb*设备 获取设备访问句柄

linux遵循一切皆文件的思想,是通过文件的方式访问硬件驱动,图形fb设备也是如此,应用需要先open设备节点/dev/fb*拿到访问句柄,然后通过ioctl命令访问设备句柄,来触发对逻辑驱动的行为。

int fb = open("/dev/fb0", O_RDWR);

2、设置显示相关属性fb_var_screeninfo/fb_fix_screeninfon:

拿到文件句柄后,我们需要先设置显示相关属性,比如 要访问的显存 分辨率,像素格式。
通过FBIOGET_VSCREENINFO的ioctl命令获取默认可变信息,修改其中需要调整部分,通过FBIOSET_VSCREENINFO设置属性;

// 可变显示信息 关键参数
struct fb_var_screeninfo {
	// 在整个显存中的 需要真正显示内容的 可见分辨率(可以认为是一帧ui的分辨率,如1280x720)
	__u32 xres;			/* visible resolution		*/
	__u32 yres;
	// 整个显存的分辨率,注意xres_virtual》= xres
	// 举例 当 xres_virtual = res yres_virtual = yres*2 ,相当于显存里面有2帧ui的buffer,通过切换yoffset来表示要显示哪一块  
	__u32 xres_virtual;		/* virtual resolution		*/
	__u32 yres_virtual;
	// 在整块显存中 显示的像素点 在 x方向 和 y方向的偏移(注意相当于是 fb逻辑访问的首地址计算偏移,不是在画面中的显示位置偏移)
	__u32 xoffset;			/* offset from virtual to visible */
	__u32 yoffset;			/* resolution			*/
	// 每个像素点的的bit长度 和 每个分量排布 和 顺序,既描述每个点在内存中的表达方式
	int bits_per_pixel;
	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
	struct fb_bitfield green;
	struct fb_bitfield blue;
	struct fb_bitfield transp;
	//..省略..
};
// 获取默认的可变信息 
ioctl(fb,FBIOGET_VSCREENINFO,&fb_var);
// 修改var可变参数
fb_var.xres = 1280;
fb_var.yres = 720;
// 设置可变信息
ioctl(fb,FBIOSET_VSCREENINFO,&fb_var);

在这里插入图片描述
设置完fb_var_screeninfo后,还需要获取fb_fix_screeninfo固定的显示参数,其中最关键的就是显存大小 和 每行的line_length。
注意这里顺序不可变,要先设置var可变参数,再获取fix固定参数,者里面的原因是fix参数可以理解为 不可设置参数,并不是不变,而它往往在驱动中是根据var参数 由图形驱动决策。 举个例子,line_length也叫stirde,表示每行占画面用的bytes,这往往和var中的xres水平分辨率和bits_per_pixel每个像素bits决定,设置var后驱动会更新它,但这里注意 由于逻辑访问内存存在对齐的约束,line_length >= xres * bits_per_pixel / 8, 多出来的部分是对齐必要的额外内存,这个策略只能由底层逻辑驱动自己按能力决策;

// 获取固定的显示参数
ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
// 关键参数
struct fb_fix_screeninfo {
	// 显存内存总长度
	__u32 smem_len;			/* Length of frame buffer mem */
	// 显存每行数据的stride长度,即每行的bytes;
	__u32 line_length;		/* length of a line in bytes    */
	//..省略..
};

3、映射显存,并始绘制内容

// 通过mmap设备句柄,拿到图形的显存虚拟首地址地址
 char *addr = mmap(NULL, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 直接通过虚拟地址进行绘制

当前显示的坐标是xoffset和yoffset,你可以直接往上面绘制(由于边绘边显示 可能会裂屏),也可以往其新坐标绘制再通知刷新。绘制的起始地址addrdraw = 显存首地址addr + 垂直方向搬移行数 yoffset * 每一行的字节数 line_length + 水平方向的像素偏移 xoffset * 每个像素的字节数量 bits_per_pixel / 8;

4、通知驱动刷新显示位置(linux标准PAN_DISPLAY方式)

linux提供的标准PAN_DISPLAY刷新方式,通知驱动显示按设置入参vinfo中的xoffset和yoffset进行刷新显示;

// 按vinfo中xoffset和yoffset进行刷新
ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);

三、framebuffer驱动关键设计

1、硬件设计抽象 与 版本差异化处理

既然fb是一个硬件驱动,自然就涉及对硬件的抽象过程,如何定义 逻辑ip链路进行软件概念抽象自然是一个关键设计。同时,如果要用一个驱动代码 去 适配多款芯片 要考虑如何适配 逻辑上的差异,比如 层数量,层能力差异等,多示例的管理与通路绑定设计。

2、中断业务设计与优化

一般的显示相关驱动,无论视频还是图形,其逻辑配置动力都来源与时序显示中断,因为这样才能够和画面切换匹配。自然有很多内容切换需要在中断里面完成,保证 逻辑链路 切换生效时间点。这里就会设计中断耗时和性能的优化设计。

3、Android框架引入的fence 私有接口和Vsync

在android框架下引入buffer的fence概念,来通知fb驱动 这个buffer什么时候可以读取,由于标准的linux PAN_DISPLAY刷新方式是通过 x y坐标来通知,而不支持fence传递,android fence一般是走私有接口,这里传递fence和其他的一些私有信息(这种接口类似que帧设计,把帧送入fb,fb自己按中断决策刷新地址)。
android的SufaceFlinger系统服务,需要利用时序刷新时间来做Vsync支持,因此往往fb还要提供接口提供上次wait显示时序中断。

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值