Android GDI之SurfaceFlinger
SurfaceFinger按英文翻译过来就是Surface投递者。SufaceFlinger的构成并不是太复杂,复杂的是他的客户端建构。SufaceFlinger主要功能是:
1) 将Layers (Surfaces) 内容的刷新到屏幕上
2) 维持Layer的Zorder序列,并对Layer最终输出做出裁剪计算。
3) 响应Client要求,创建Layer与客户端的Surface建立连接
4) 接收Client要求,修改Layer属性(输出大小,Alpha等设定)
但是作为投递者的实际意义,我们首先需要知道的是如何投递,投掷物,投递路线,投递目的地。
1 SurfaceFlinger的基本组成框架
SurfaceFlinger管理对象为:
mClientsMap:管理客户端与服务端的连接。
ISurface,IsurfaceComposer:AIDL调用接口实例
mLayerMap:服务端的Surface的管理对象。
mCurrentState.layersSortedByZ :以Surface的Z-order序列排列的Layer数组。
graphicPlane 缓冲区输出管理
OpenGL ES:图形计算,图像合成等图形库。
gralloc.xxx.so这是个跟平台相关的图形缓冲区管理器。
pmem Device:提供共享内存,在这里只是在gralloc.xxx.so可见,在上层被gralloc.xxx.so抽象了。
2 SurfaceFinger Client和服务端对象关系图
Client端与SurfaceFlinger连接图:
Client对象:一般的在客户端都是通过SurfaceComposerClient来跟SurfaceFlinger打交道。
3 主要对象说明
3.1 DisplayHardware &FrameBuffer
首先SurfaceFlinger需要操作到屏幕,需要建立一个屏幕硬件缓冲区管理框架。Android在设计支持时,考虑多个屏幕的情况,引入了graphicPlane的概念。在SurfaceFlinger上有一个graphicPlane数组,每一个graphicPlane对象都对应一个DisplayHardware.在当前的Android(2.1)版本的设计中,系统支持一个graphicPlane,所以也就支持一个DisplayHardware。
SurfaceFlinger,Hardware硬件缓冲区的数据结构关系图。
3.2 Layer
method:setBuffer 在SurfaceFlinger端建立显示缓冲区。这里的缓冲区是指的HW性质的,PMEM设备文件映射的内存。
1) layer的绘制
void Layer::onDraw(const Region& clip) const
{
int index = mFrontBufferIndex;
GLuint textureName = mTextures[index].name;
…
drawWithOpenGL(clip, mTextures[index]);
}
3.2 mCurrentState.layersSortedByZ
以Surface的Z-order序列排列的LayerBase数组,该数组是层显示遮挡的依据。在每个层计算自己的可见区域时,从Z-order 顶层开始计算,是考虑到遮挡区域的裁减,自己之前层的可见区域就是自己的不可见区域。而绘制Layer时,则从Z-order底层开始绘制,这个考虑到透明层的叠加。
4 SurfaceFlinger的运行框架
我们从前面的章节<Android Service>的基本原理可以知道,SurfaceFlinger的运行框架存在于:threadLoop,他是SurfaceFlinger的主循环体。SurfaceFlinger在进入主体循环之前会首先运行:SurfaceFlinger::readyToRun()。
4.1 SurfaceFlinger::readyToRun()
(1)建立GraphicPanle
(2)建立FrameBufferHardware(确定输出目标)
初始化:OpenGL ES
建立兼容的mainSurface.利用eglCreateWindowSurface。
建立OpenGL ES进程上下文。
建立主Surface(OpenGL ES)。 DisplayHardware的Init()@DisplayHardware.cpp函数对OpenGL做了初始化,并创建立主Surface。为什么叫主Surface,因为所有的Layer在绘制时,都需要先绘制在这个主Surface上,最后系统才将主Surface的内容”投掷”到真正的屏幕上。
(3) 主Surface的绑定
1)在DisplayHandware初始完毕后,hw.makeCurrent()将主Surface,OpenGL ES进程上下文绑定到SurfaceFlinger的上下文中,
2)之后所有的SurfaceFlinger进程中使用EGL的所有的操作目的地都是mSurface@DisplayHardware。
这样,在OpenGL绘制图形时,主Surface被记录在进程的上下文中,所以看不到显示的主Surfce相关参数的传递。下面是Layer-Draw,Hardware.flip的动作示意图:
4.2 ThreadLoop
(1)handleTransaction(…):主要计算每个Layer有无属性修改,如果有修改着内用需要重画。
(2)handlePageFlip()
computeVisibleRegions:根据Z-Order序列计算每个Layer的可见区域和被覆盖区域。裁剪输出范围计算-
在生成裁剪区域的时候,根据Z_order依次,每个Layer在计算自己在屏幕的可显示区域时,需要经历如下步骤:
1)以自己的W,H给出自己初始的可见区域
2)减去自己上面窗口所覆盖的区域
在绘制时,Layer将根据自己的可将区域做相应的区域数据Copy。
(3)handleRepaint()
composeSurfaces(需要刷新区域):
根据每个Layer的可见区域与需要刷新区域的交集区域从Z-Order序列从底部开始绘制到主Surface上。
(4)postFramebuffer()
(DisplayHardware)hw.flip(mInvalidRegion);
eglSwapBuffers(display,mSurface) :将mSruface投递到屏幕。
5 总结
现在SurfaceFlinger干的事情利用下面的示意图表示出来:
SurfaceFlinger对象建立过程示意
1 SurfaceSession的建立
客户端请求建立Surface时,首先在要与SurfaceFlinger建立一个Session,然后再Session上建立一个Connection通过概念返回Bclient对象。WindowManagerService在添加第一个窗口前会检查SurfaceSession是否建立,如何没有建立,将会新建立一个实例来代表与SurfaceFlinger的一个连接。
new SurfaceSession()@windowAddedLocked() @WindowManagerService.java。
SurfaceSession的建立过程大部分是在C++ Native空间中完成的,表现在SurfaceSession的初始化函数:init()本地函数上。从下面的初始化函数可以看到:
Init()<->SurfaceSession_init@android_view_Surface.cpp
new SurfaceComposerClient
SurfaceSession在C++Native空间建立一个SurfaceComposerClient实例。而该实例的建立实现了如下的与SurfaceFlinger通讯基础:
(1)建立了代理SurfaceFlinger服务的代理服务端
(2)建立了IsurfaceFlingerClient连接,在SurfaceFlinger端建立了对应的Client,并将BClient返回给WindowManagerService。
2 Surface的建立
在WindowManagerService中WindowState类中,我们知道每个主窗口子啊需要是都需要建立一个Surface与之对应。win.createSurfaceLocked()@relayoutWindow
Surface.java
Init()< -- >Surface_init(….,session,pid,dpy,w,h,format)@android_view_Surface.cpp
SurfaceControl surface(client->createSurface
在mClient的连接上:建立ISurface接口:
M_Client->greateSurface(...)@
Bclient ::createSurface(mId...)@SurfaceFlinger.cpp
mFlinger->createSurface(clientid....)
createNormalSurfaceLocked
*createNormalSurfaceLocked:建立一个Layer分配显示内存
*createPushBuffersSurfaceLocked:建立一个LayBuffer但是不分配显示内存。
Surface&Canvas
Canvas为在画布的意思。Android上层的作图几乎都通过Canvas实例来完成,其实Canvas更多是一种接口的包装。drawPaints ,drawPoints,drawRect,drawBitmap ...
1 Canvas与Surface之间本质关系
对于本节,我们不去研究Skia图形引擎本身,我们需要了解的我们的所做的图形到底放置到了那个地方,并且这个Canvas如何与Surface连接在一起的。
Canvas(Java)在C++Native层有一个Native Canvas的C++对象所对应。
lockCanvas()@java
Surface_lockCanvas@android_view_Surface.cpp
SurfaceControl->new Surface(control) @Surface.cpp
Surface: lock操作:
GraphicBuffer :lock
getBufferMapper().lock<-> GraphicBufferMapper ::lock
mAllocMod->lock<->gralloc_module_t::lock
通过SurfaceLock可取得Surface(mLockedBuffe)所对应的图形缓冲区地址。
(1) 建立与SkCanvas连接的位图设备,而该位图使用上面取得的图形缓冲区地址做自己的位图内存。
(2) 设置SkCanvas的作图目标设备为该位图。
通过该过程就建立起了SurfaceControl与Canvas之间的联系。
2 View:OnDraw的本源
不是使用OpenGL绘制时,Android在View属性发生变化,新建View时,或者Z-order发生变化时,需要对系统屏幕上的View重新绘制,此时我们的View会执行OnDraw(canvas),这个根源在哪里呢?
ViewRoot.Java
performTraversals(..)
…
draw()
canvas = surface.lockCanvas(dirty);
…
mView.draw(canvas);
draw(cavas)@view.java
background.draw(canvas);
onDraw(cavas)
dispatchDraw(cavas)
onDrawScrolbars(cavas)
surface.unlockCanvasAndPost(canvas);