http://blog.csdn.net/wealoong/article/details/7987770
Android display架构分析五-Display接口介绍
1、User Space display接口
在Android平台下,应用程序面对的显示部分的接口就是HAL,参考copybit.cpp (qcom\diaplay\libcopybit),具体接口如下介绍:
open_copybit 初始化相关变量,并调用open(“/dev/graphics/fb0″, O_RDWR, 0);打开fb设备。
set_parameter_copybit 设置各种操作参数,如rotate、alpha、dither等。
stretch_copybit Copy一块数据(Rectangle)到显存,然后并命令msm_fb进行显示。
close_copybit 调用close(ctx->mFD);关闭fb设备。
Note:另外,应用程序在使用上面接口之前,需要调用mapFrameBuffer接口(EGLDisplaySurface.cpp),其功能如下:
1、 初始化显示相关参数,并设置到底层。
2、 映射出显存的虚拟地址。
2、Kernel display接口
Kernel部分显示的接口全部都在fbmem.c中,这里详细介绍一下:
fb_open 打开Linux下fb设备。
fb_read/fb_write 读写显存中的数据
fb_ioctl 对显示设备的命令操作。如get或set一些显示参数、通知底层进行刷屏等。
在典型应用中,画屏的一般步骤如下:
1. 打开/dev/fb设备文件。
2. 用ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
3. 将屏幕缓冲区映射到用户空间。
4. 映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。
典型程序段如下:
- #include <linux/fb.h>
- int main()
- {
- int fbfd = 0;
- struct fb_var_screeninfo vinfo;
- struct fb_fix_screeninfo finfo;
- long int screensize = 0;
- /*打开设备文件*/
- fbfd = open("/dev/fb0", O_RDWR);
- /*取得屏幕相关参数*/
- ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo);
- ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo); //该函数最后会将参数传递给finfo、vinfo
- /*计算屏幕缓冲区大小*/
- screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
- /*映射屏幕缓冲区到用户地址空间*/
- fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED, fbfd, 0);
- /*下面可通过fbp指针读写缓冲区*/
- ...
- }
3典型应用flow分析
在不同应用程序中,上层的调用会有所不同,比如Andriod下会选择应用程序跳过Linux fb操作层,直接操作显卡驱动层,称之为BLT accelerator。
下面看一下Android平台下画屏的操作流程。
1、 通过mapFrameBuffer直接把用户空间的数据映射到显存中。
2、 调用HAL中的stretch函数直接命令MSM设备提取显存数据然后送入MDP PPP进行处理并经MDDI接口送到外围LCD组件。
具体的函数调用流程如下:
- copybit_open(); //打开BlitEngine,同时也打开fb设备
- mapFrameBuffer(); //设置显示参数,同时得到显存虚拟地址
- copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it); //通知底层去刷屏
- stretch_copybit
- 调用:status = msm_copybit(ctx, &list);
- ->msm_copybit
- 调用int err = ioctl(dev->mFD, MSMFB_BLIT, (struct mdp_blit_req_list const*)list);
- //此处应该没有调用到:fb_ioctl(),直接跳到msm_fb_ioctl
- ->msm_fb_ioctl(MSMFB_BLIT) //此处将调用msm_fb.c文件中msm_fb_ioctl()函数中的switch结构,跳入MSMFB_BLIT分支调用:
- ->case MSMFB_BLIT:
- ret = msmfb_blit(info, argp);
- -> int ret = mdp_blit(info, &(req_list[i]));
- -> mdp_ppp_blit
- -> mdp_start_ppp
- ->MDP&MDDI HW operation
本部分介绍的完全是用户空间显示部分的架构,与kernel并没有直接的联系,主要是JNI以下到HAL以上的部分。
1、Surface manager(surface flinger)简介
Surface manager是用户空间中framework下libraries中负责显示相关的一个模块。如下:
当系统同时执行多个应用程序时,Surface Manager会负责管理显示与存取操作间的互动,另外也负责将2D绘图与3D绘图进行显示上的合成。
surface manager 可以准备一块 surface(可以看作一个layer),把 surface 的 fd (一块内存) 传给一个 app,让 app 可以在上面作画。 典型应用如下:
2、Surface manager架构分析
Android中的图形系统采用Client/Server架构,如下:
Client端:应用程序相关部分。代码分为两部分,一部分是由Java提供的供应用使用的api,另一部分则是由c++写成的底层实现。
Server端:即SurfaceFlinger,负责合成并送入buffer显示。其主要由c++代码编写而成。
Client和Server之前通过Binder的IPC方式进行通信,总体结构图如下:
如上图所示,Surface的client部分其实是提供给各应用程序进行画图操作的一个桥梁,该桥梁通过binder通向server端的Surfaceflinger,Surfaceflinger负责合成各个surface,然后把buffer传送到framebuffer端进行底层显示。其中每个surface对应2个buffer,一个front buffer, 一个back buffer,更新时,数据更新在back buffer上,需要显示时,则将back buffer和front buffer互换。
下一部分我们重点研究一下Surfaceflinger。
1、 Thread本身处理部分,包括初始化以及thread loop。
2、 Binder部分,负责接收上层应用的各个设置和命令,并反馈状态标志给上层。
3、 与底层的交互,负责调用底层接口(HAL)。
a、 Binder接收到应用程序的命令(如创建surface、设置参数等),传递给flinger。
b、 Flinger完成对应命令后将相关结果状态反馈给上层。
c、 在处理上层命令过程中,根据需要设置event(主要和显示有关),通知Thread Loop进行处理。
d、 Flinger根据上层命令通知底层进行处理(主要是设置一些参数,Layer、position等)
e、 Thread Loop中进行surface的合成并通知底层进行显示(Post buffer)。
f、 DisplayHardware层根据flinger命令调用HAL进行HW的操作。
下面来具体分析一些SurfaceFlinger中重要的处理函数以及surface、Layer的属性
1)、readToRun
SurfaceFlinger thread的初始化函数,主要任务是分配内存和设置底层接口(EGL&HAL)。
- status_t SurfaceFlinger::readyToRun()
- {
- …
- mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY); //为IPC分配共享内存
- …
- mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20); //为flinger分配heap,大小为8M,存放具体的显示数据
- {
- // initialize the main display
- GraphicPlane& plane(graphicPlane(dpy));
- DisplayHardware* const hw = new DisplayHardware(this, dpy);
- plane.setDisplayHardware(hw); //保存显示接口
- }
- //获取显示相关参数
- const GraphicPlane& plane(graphicPlane(dpy));
- const DisplayHardware& hw = plane.displayHardware();
- const uint32_t w = hw.getWidth();
- const uint32_t h = hw.getHeight();
- const uint32_t f = hw.getFormat();
- …
- // Initialize OpenGL|ES
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, 0);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- …
- }
-
此部分可以参考文章:SurfaceFlinger启动过程分析 Android系统Surface机制的SurfaceFlinger服务的启动过程分析
2)、ThreadLoop
Surfaceflinger的loop函数,主要是等待其他接口发送的event,进行显示数据的合成以及显示
- bool SurfaceFlinger::threadLoop()
- {
- waitForEvent(); //等待其他接口的signal event
- …
- handlePageFlip(); //处理翻页机制
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- if (LIKELY(hw.canDraw()))
- {
- // repaint the framebuffer (if needed)
- handleRepaint(); //合并所有layer并填充到buffer中去
- …
- postFramebuffer(); //互换front buffer和back buffer,调用EGL接口进行显示
- }
- …
- }
在最新的4.1的代码中,threadLoop只调用了waitForEvent,其他部分的实现均放在onMessageReceived()函数中实现【SurfaceFlinger.cpp】
谷歌在Android native层实现的一个异步消息机制,在这个机制中几乎不存在同步锁,所有的处理都是异步的,将变量封装到一个消息AMessage结构体中,然后放到队列中去,后台专门有一个线程会从这个队列中取出消息然后执行,执行函数就是onMessageReceived,这个函数中会有很多分支,用于处理不同的消息;在很多类中都会有各种消息post出来,而后台的异步消息处理线程又是怎么知道发送给哪个类的onMessageReceived函数处理呢,要搞懂这个问题,就需要把谷歌实现的这个异步消息处理框架搞明白,参考文章:http://blog.sina.com.cn/s/blog_645b74b90101cx69.html
3)、createSurface
提供给应用程序的主要接口,该接口可以创建一个surface,底层会根据参数创建layer以及分配内存,surface相关参数会反馈给上层
- sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
- ISurfaceFlingerClient::surface_data_t* params,
- DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t flags)
- {
- …
- int32_t id = c->generateId(pid);
- if (uint32_t(id) >= NUM_LAYERS_MAX) //NUM_LAYERS_MAX=31
- {
- LOGE("createSurface() failed, generateId = %d", id);
- return
- }
- …
- layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags); //创建layer,根据参数(宽高格式)分配内存(共2个buffer:front/back buffer)
- if (layer)
- {
- setTransactionFlags(eTransactionNeeded);
- surfaceHandle = layer->getSurface(); //创建surface
- if (surfaceHandle != 0)
- surfaceHandle->getSurfaceData(params); //创建的surface参数反馈给应用层
- }
- }
4)、setClientState
处理上层的各个命令,并根据flag设置event通知Threadloop进行处理
- status_t SurfaceFlinger::setClientState(
- ClientID cid,
- int32_t count,
- const layer_state_t* states)
- {
- Mutex::Autolock _l(mStateLock);
- uint32_t flags = 0;
- cid <<= 16;
- for (int i=0 ; i<count ; i++) //检测所有存在layer的状态标志
- {
- const layer_state_t& s = states[i];
- LayerBaseClient* layer = getLayerUser_l(s.surface | cid);
- if (layer)
- {
- const uint32_t what = s.what; // 检测应用层是否设置各个标志,如果有则通知底层完成对应操作,并通知ThreadLoop做对应的处理
- if (what & eDestroyed) //删除该层Layer
- {
- if (removeLayer_l(layer) == NO_ERROR)
- {
- flags |= eTransactionNeeded;
- continue;
- }
- }
- if (what & ePositionChanged) //显示位置变化
- {
- if (layer->setPosition(s.x, s.y))
- flags |= eTraversalNeeded;
- }
- if (what & eLayerChanged) //Layer改变
- {
- if (layer->setLayer(s.z))
- {
- mCurrentState.layersSortedByZ.reorder(
- layer, &Layer::compareCurrentStateZ);
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- if (what & eSizeChanged)
- {
- if (layer->setSize(s.w, s.h)) //设置宽高变化
- flags |= eTraversalNeeded;
- }
- if (what & eAlphaChanged) { //设置Alpha效果
- if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
- flags |= eTraversalNeeded;
- }
- if (what & eMatrixChanged) {//矩阵参数变化
- if (layer->setMatrix(s.matrix))
- flags |= eTraversalNeeded;
- }
- if (what & eTransparentRegionChanged) { //显示区域变化
- if (layer->setTransparentRegionHint(s.transparentRegion))
- flags |= eTraversalNeeded;
- }
- if (what & eVisibilityChanged) {//是否显示
- if (layer->setFlags(s.flags, s.mask))
- flags |= eTraversalNeeded;
- }
- }
- }
- if (flags)
- { setTransactionFlags(flags); //通过signal通知ThreadLoop
- }
- return NO_ERROR;
- }
5)、composeSurfaces ,在 handleRepaint()中调用到
该接口在Threadloop中被调用,负责将所有存在的surface进行合并,OpenGl模块负责这个部分。
6)、postFramebuffer
该接口在Threadloop中被调用,负责将合成好的数据(存于back buffer中)推入在front buffer中,然后调用HAL接口命令底层显示。
7)、从3中可知,上层每创建一个surface的时候,底层都会同时创建一个layer,下面看一下surface及layer的相关属性。
Note:code中相关结构体太大,就不全部罗列出来了
A、Surface相关属性(详细参考文件surface.h)
a1:SurfaceID: 根据此ID把相关surface和layer对应起来
a2:SurfaceInfo 包括宽高格式等信息
a3:2个buffer指针、buffer索引等信息
B、Layer相关属性(详细参考文件layer.h/layerbase.h/layerbitmap.h)
包括Layer的ID、宽高、位置、layer、alpha指、前后buffer地址及索引、layer的状态信息(如eFlipRequested、eBusy、eLocked等)
1添加新的Display Driver的工作内容
参考上面linux下fb设备的软件架构,可以知道,要加入一个新的MDDI 接口的LCM,Driver的工作就是要提供自己的mddi_xxxx.c(在这次porting的过程中,为了节省时间,我们直接修改了mddi_toshiba.c),并且完成和这个lcd相关的HWr的初始化。主要的工作包括:
A、初始化和LCD / LCD背光相关的IO以及电源;
B、编写初始化函数 。主要是初始化LCD控制器,这个一般LCD厂商会提供;然后分配显存,这个高通release过来的code已经包含这个动作了,最后是初始化一个fb_info的结构体,在这里主要是把LCD的一些信息登记进来。
C、把LCD的设备以及驱动注册到系统中去。(这里因为是替换现有的驱动,所以相关修改的部分不多。)
上述B、C部分代码请参考kernel\drivers\video\msm\mddi_toshiba.c。
2Display Driver开发过程
1.2.1配置Power和IO
更改一些GPIO的配置以及一些电源的电平配置;然后通过实际测量,确保一下信号正常:
A、供给LCD以及MDDI Bridge的电源;
B、MDDI Bridge以及LCD reset信号;
C、控制背光IC的GPIO工作正常(背光不打开,无法调试LCD)。
1.2.2Porting LCD初始化序列
LCD init的code以及外围MDDI Bridge的初始化code,都可以之前Boston Windows Mobile系统的code base中获得;把这部分code移植到mddi_Toshiba.c中,并更改相应的图像格式、分辨率等配置,编译通过。LCD初始化部分就算基本完成。
1.2.3LCD初始化过程的调试
由于硬件在之前Boston load是可以工作的,可以认为硬件连接等没有问题,所以只需关注软件部分就行。
Display部分软件调试过程如下:
A、 开机后,量一下GPIO是否为code中配置预期的状态(可确保code中的
GPIO接口工作正常);
B、 量一下各个电源是否都处于Code中定义的电平值。这些都OK后,背光
是会亮的(背光的控制比较简单,一个GPIO即可);
C、 这个时候如果LCD以及MDDI Bridge有被正常初始化的话,屏幕上是会
看出来的。反之,如果屏幕没有显示,需要用JTAG跟一下mddi_Toshiba.c中的初始化函数是否在开机的时候有被调用过。
目前版本中,是根据外围MDDI Bridge中读到的的厂商号来决定加载哪个驱动模块的。在本次调试中,bootloader中可以正确读到厂商号,所以bootloader中对于LCD的初始化是有做的,所以屏幕看到的状态就是LCD初始化后的样子(花屏)。但Kernel起来后,并没有其他显示,用JTAG跟了后发现,Kernel中MODULE INIT中读不到正确的厂商号,所以说后面的driver没有被加载。接着发现如果在bootloader中如果不做MDDI Bridge的初始化,的话后面的MODULE INIT就可正常运行,该问题目前还没有澄清(现在暂时先把bootloader中的init disable掉)。
1.2.4LCD的调整
初始化正常后,屏幕会显示UI的相关画面,但明显颜色、位置都不对。
这个可能是数据类型配置不对导致的,即MDP输出的类型、MDDI配置的类型以、LCD接收的类型不匹配导致,也有可能是RGB的顺序不对导致(可配置成BGR)。经过调试后,把MDP端输出的格式配置成RGB565,同时外围MDDI Bridge以及LCD的input格式也配置成RGB565,这时显示色彩正常了。
如果位置或者方向不对,比如说上下或是左右颠倒,可以更改LCD的配置中的扫描方向即可。
1.2.5其他
后续发现一个问题,播放video的时候颜色都是黑白的。
这个问题很容易让人误解,按照正常的理解,video decode出来的数据为YCbCr,Y为亮度信号,CbCr为色差信号,如果只有Y信号的话颜色应该就是黑白的。所以有2个怀疑点,一个是decode出来的数据有误,另一个是MDDI Bridge误把输入的YcbCr信号当作RGB信号进行出来,这个也是有可能的。但很快第二个怀疑点被排除了(因为单更改MDDI input格式后还是不能解决问题)。
后来又详细的看了显示部分的代码,并用JTAG追踪video播放的时候用的显示接口,发现目前所有的显示接口输出的格式都是RGB格式,也就是说在通过MDP之前YcbCr已经被转化过;而MDP里的转换功能并没有使用,MDP只是被当作一个DMA完成数据的直接传输,文档中叫做Bypasse。
YcbCr到RGB的转换是由Android的lib来完成。发了个SR给高通,高通的回复也确认了,在6.3.50中,Android上层缺少这个lib(copybit.default.so),6.3.60之后的版本经解决了这个问题。
显示部分的几个问题这几天通过实际测试澄清了一下,主要是下图中各个模块的使用状况以及HAL层几个模块的调用流程。以问题的方式描述如下:
1、Ap是怎么进行显示的?
Surfaceflinger负责所有上层的显示处理,对于AP(2D或是3D的应用程序)而言,只要到surfaceflinger中创建surface,设置好参数,接下来都是统一交给surfaceflinger进行处理
2、Surface是怎么管理多个surface的?
不管有多少个surface,最终送到显示部分的只能是屏幕大小数据,surfaceflinger中利用MDP或是GPU进行多个surface的合成处理,普通的合成MDP就可完成,但如果是复杂的比如3D的应用等就必须使用GPU,最终合成的好数据会被送到framebuffer中。
3、Framebuffer是什么?
Framebuffer是Linux中为显示数据分配的一块显存(fb设备中),通常大小是一整个屏幕数据的两倍,对于上层AP而言,只需要将要显示的数据丢到framebuffer中就OK了,但此时显示数据并未真正的被送到LCD上,而是暂存在framebuffer中而已。
4、上层是通过什么方式将显示内容送到framebuffer的?
有2个方式(二选一,不会同时在运行):
A、普通的显示,使用copybit(MDP)(未使用GPU)
Surfaceflinger通过copybit将要显示的数据送到framebuffer。
Note:copybit可以看做是MDP PPP的接口,它提供了MDP的功能,如多个layer合成,scale、rotate等。
其接口在:android/hardware/msm7k/libcopybit/copybit.cpp
B、使用GPU(即使用图中的Graphics driver)
当进行复杂的显示处理时,比如3D的应用,GPU把处理好的数据直接丢到framebuffer中,和MDP没有任何关系
5、Framebuffer中的数据是如何被送到LCD显示的?
图中的Gralloc完成的。
Gralloc有2个功能:
一个是和copybit相同的,里面有MDP PPP的接口(目前没有使用)
另一个则是刷屏(整屏刷)的接口,即将framebuffer中的数据送到lcd上,调用的是MDP DMA的接口
这部分的code在android/hardware/msm7k/libgralloc-qsd8k目录下,之前没有留意,以为没有使用。现在可以看出开机初始化后就创建了disp_loop thread,里面的操作就是调用系统接口ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info)将数据送到lcd
Note:送数据的时候是2个buffer切换的
另外,上层surfaceflinger也是通过Gralloc中的接口获知屏幕的大小,调用接口为ioctl(fd, FBIOGET_VSCREENINFO, &info),info中的屏幕宽高对应的就是底层driver设置的宽高值
6、OpenGL是什么?
它是一个图像处理引擎,当需要一些复杂的显示(2D/3D)操作时会用到它。它分为SW方案和HW方案,
软件方案就是图中的libagl.so,对应到目前项目中是libGLES_android.so,它可以完成简单的2D(文字,icon等)处理,通过trace看目前大部分显示操作都是它来完成的。
Note:它是软件方案,处理好的数据是通过copybit送到framebuffer的,而不是GPU。其接口部分参考:android/frameworks/base/opengl/libagl
HW方案就是图中的Graphics driver,它通过使用GPU硬件来完成图像处理,处理后的数据直接送到framebuffer中。其接口部分参考android/frameworks/base/opengl/libs(有几个版本)
7、OpenGL在项目中是如何配置的?
在android/vendor/qcom/msm7627_ffa目录下有一个egl.cfg文件,里面指定了当前版本中的OpenGL信息,目前如下:
0 0 android 第一行代表该codebase支持SW 方案的OpenGL,是android default的
0 1 adreno200 第二行代表该codebase也支持HW方案的OpenGL,是高通的adreno引擎
如果该cfg文件为空,则只支持default的SW方案。
如果2个方案都在,上层将根据实际应用自行选择使用其一。
该部分请参考:android/frameworks/base/opengl/libs/EGL/loader.cpp