您的位置:首页 > 其它

安卓图像更新学习总结

2017-09-14 18:37 337 查看
总体上是图像生产者,如应用的一个窗口(activity)的绘制、程序调用OpenGL的各种”draw”函数等,把图像数据传给相应的处理接口。2D的一般使用Canvas,3D的可以直接调用OpenGL ES。但是两种方法图像都绘制到一个surface上,关于绘制操作,如OpenGL的图形库有一个graphics pipeline的流程,主要为从把点的坐标转换到normalized device coordinate,即x,y,z是-1.0到正1.0的坐标系中,再裁剪可视区域,进而以Z坐标确定可视图层顺序,rasterization,即像素化,这样一个流程。OpenGL中的framebuffer(帧缓存)已经是像素化之后的位图了,这说明以效果为主要目的的处理图像已经GPU端完成,在Linux内核的framebuffer层之上。但这个“graphics
pipeline”的概念不仅仅是OpenGL独有的,操作系统如安卓等在传输和处理图像时也与此相近,只是最终把图像绘制到surface上。

此时的问题是如何把图像传给屏幕端。一般的应用产生的所有图像都会传给surfaceflinger,后者再选择性的把图像传给显示控制器。显示控制器一般是芯片上的一个硬件,它的驱动软件和安卓框架层的接口是HWComposer,由该库与之交互。

安卓通用的模型大致如此,对于我们使用的MTK的芯片,因为很多模块是闭源的或者完全没有说明,我只能作推测。应该是HWCompser接收到surfaceflinger的事件后通过HWC的库调用内核的fb接口,然后显控以DMA的方式读取帧缓存,进行”overlay”的处理,最终在驱动层的overlay处理图像后通过DSI传给屏幕。

现在介绍我理解的一些模块的工作方式:

由于应用传输图像给surfaceflinger的操作不涉及内核,在此不作分析。Surfaceflinger初始化时会创建一个HWComposer实例,等开始上层通过BufferQueue把缓存传给Surfaceflinger,SF会调用hwc的方法prepare来决定哪些图层需要给显示控制器处理,然后把这些图层收集起来通过postFramebuffer方法更新帧缓存,这里的帧缓存并非立即就更新到屏幕上,而是调用mHwc->commit(hwcId)把数据给HWComposer;其余的图层由surfaceflinger调用OpenGL来合成。SF会设置一个“Fence“(围栏)来表示对每一帧操作的开始与完成。AOSP文档(source.android.com)上指出,surfaceflinger曾经直接把帧缓存往比如/dev/graphics/fb0里写,但很久前就不再这么做了。内核里有drivers/gpu/drm/mediatek,里面有mtk_drm_drv,mtk_drm_fb等,但Linux的DRM(direct
rendering manager)在安卓里应该用不到,因为计算机上的XWindow或者Wayland用的协议没有Fence的概念,所以这些代码应该没什么作用,但我不很确定是干什么用的,于是从SF到内核层至少有两个通路:

1) surfaceflinger -> HWComposer-> graphics/fb0(mtkfb)->overlay

2) surfaceflinger->GPU->overlay

第一条通路显然是2D绘制。大多数安卓设备都有/dev/graphics/fb0这个framebuffer设备,经分析我们的MTK的系统中fb0最终就是mtkfb.c在驱动,图像设备有major number 29,adb shell执行ls –l /dev/graphics/fb0可知minor number 为0,在对应/sys/dev/char/29:0/device/driver中可以找到该设备的驱动,再通过readlink该驱动发现是bus/platform/drivers/mtkfb的链接。Mtkfb.c中并没有指定具体路径,这说明/dev/graphics/应该是安卓内核图像设备的默认位置。

然而直接写mtkfb(比如cat image > fb0)并没有作用,读(cp fb0 /sdcard/fb)可以得到1个帧(3个图层),通过ffmpeg等解码器转换为常用图片格式后发现所得往往并不是“截图“时屏幕所显示的内容,这已知有几种原因,首先有很多图层不是走的这条通路,第二是图像缓存不是一直更新,比如空闲时primary_display会切换到decouple mode。查看mtkfb.c可知,fb_ops结构并没有read和write的操作,但fb设备和内存设备类似,所以对其进行读操作应该可以复制出它内存中的数据,对其写就不会触发fb驱动的刷屏操作。除了跟primary_display、ddp*等内部互相调用的接口外,mtkfb主要以ioctl来操作,在mtkfb.h中有相应ioctl操作的定义。如果在root
shell下以用户层的ioctl来操作mtkfb(dev/graphics/fb0),比如ioctl(fd, MTKFB_META_SHOW_BOOTLOGO),我们会发现屏幕会显示随机的缓存然后恢复,通过日志可知它的调用顺序为mtkfb_ioctl->primary_display_config_input_multiple ,primary_display_trigger,而primary_display_config_input_multiple调用了_config_ovl_input,其中关于”帧“的数据结构为disp_session_input_config,该结构包含了帧大小和地址等信息,进而调用_config_ovl_input,由此可见,2D绘制的调用路径是从mtkfb到ovl。观察fb_info结构,注意到mtkfb.c中对该结构使用的最多的成员是”par”,一个自定义的指针,它指向一个mtkfb_device结构的实例,而后者携带framebuffer的内存地址。

但mtkfb_ioctl有一个疑点,执行MTKFB_CAPTURE_FRAMEBUFFER的操作并没有得到有效数据,目前还不清楚是什么原因。另外关于”decouple mode”,据说该模式类似command屏,有专门的内存用于本地刷新屏幕。在primary_display_idlemgr_*函数中可以找到在空闲时切换到decouplemode的机制。稍微联想一下我们可以发现,这decouple mode、“command”屏和OpenGL提交、更新图像的模式十分相近,都是”客户端“提交数据和指令来指示如何处理,而”服务端“有自己的内存和响应指令的机制,两者间通过某种方式交互,OpenGL
commands、mtk cmdq、mipi dsi等等。

GPU这条通路有很多机制尚不清楚,首先安卓的gralloc负责分配GPU使用的显存,但我们用的厂商实现是MTK提供的gralloc库,HWComposer也是如此(在hardware.c里dlopen加载的这些库),MTK的显示控制器如何工作也未知,只知道在内核中驱动是使用中断和DMA,在ddp_irq加上日志可看到,平时图像更新disp_irq_handler函数都会不断响应,紧接着就是RDMA的函数(ddp_ovl_get_cur_addr等),这些是在primary_display_switch_mode里一路往下调用的,由于HWComposer上层是以event(事件)的形式交互,所以primary_display的dpmgr_map_event_to_irq应该是把事件和中断对应起来,DMA是把数据读到”overlay”;

关于显示控制器和overlay:

”overlay”在计算机显示中的作用是图像直接送到显示控制器进行简单的缩放、裁剪、组合等,没有诸如OpenGL的texture mapping、blending等功能。安卓使用“overlay”的意义是有的操作简单,若调用OpenGL用GPU处理占内存多,通常在嵌入式设备和手机中GPU没有独立显存,是共用主内存的,另外GPU一次只能作一个用途,让显控来分担一些GPU的负担,可提升性能。具体哪些帧需要GPU合成哪些需要overlay来处理是由HWComposer对帧来标记决定的,MTK的平台应该是在它的HWC库里实现的算法。MTK的“ovl“有点迷惑,它既有overlay的硬件,也有最后把所有的帧送到屏幕IC的作用。所以所有的图层在两个地方有汇总过,一是surfaceflinger,另一是最后的overlay层,这也就解释了为什么虽然有的图层是用GPU合成的,但如果在MTK的overlay里(定义PHYSICAL_ROTATION_HW的宏)旋转帧缓存,所有的图像都会旋转。现在的截图和录屏无法通过framebuffer接口来实现,都是调用surfaceflinger的接口把图层处理到一个”virtual
display”上,通过BufferQueue传输给调用软件。

最后是VSYNC信号和fence,屏幕硬件和surfaceflinger是有关联的,IC会由DSI给primary_display发vsync信号,表示一帧已显示完毕,最后这个信号会给surfaceflinger,后者只在此时进行新的绘制操作,因而vsync、porch值除了和屏幕玻璃有关外,也会影响到surfaceflinger的绘制;fence的作用和mutex的目的接近,但更灵活。其内核接口为sw_sync.h,我们可以看到mtkfb.h中包含了该头文件,底层的fence以文件描述符的形式使用,可以”poll”,检查是否可读写等,以此表示和该fence关联的图像内存是否可用,这些功能在mtkfb_fence.c中实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: