SurfaceFlinger 原理分析
SurfaceFlinger是Android multimedia的一个部分,在Android 的实现中它是一个service,提供系统范围内的surface composer功能,它能够将各种应用程序的2D、3D surface进行组合。
背景
屏幕应用程序window组成每个应用程序可能对应着一个或者多个图形界面,而每个界面我们就称之为一个surface ,或者说是window ,在上面的图中我们能看到4 个surface ,一个是home 界面,还有就是红、绿、蓝分别代表的3个surface ,而两个button 实际是home surface 里面的内容。我们需要考虑一下情况:
- 每个surface 在屏幕上有它的位置、大小,然后每个surface 里面还有要显示的内容
- 各个surface 之间可能有重叠
- 我们可以想象在屏幕平面的垂直方向还有一个Z 轴,所有的surface 根据在Z 轴上的坐标来确定前后,这样就可以描述各个surface 之间的上下覆盖关系了,而这个在Z 轴上的顺序,图形上有个专业术语叫Z-order 。
2.我们需要一个结构来记录应用程序界面的位置,大小,以及一个buffer 来记录需要显示的内容,所以这就是我们 surface 的概念,surface 实际我们可以把它理解成一个容器,这个容器记录着应用程序界面的控制信息,比如说大小、位置,而它还有buffer 来专门存储需要显示的内容。 - 当存在图形重合或者有些surface 还带有透明信息的时候,就需要 SurfaceFlinger 来解决问题,它要把各个surface 组合(compose/merge)成一个main Surface ,最后将Main Surface 的内容发送给FB/V4l2 Output ,这样屏幕上就能看到我们想要的效果。
在实际中对这些Surface 进行merge 可以采用两种方式,一种就是采用软件的形式来merge ,还一种就是采用硬件的方式,软件的方式就是我们的SurfaceFlinger ,而硬件的方式就是Overlay 。
Overlay
因为硬件merge 内容相对简单,我们首先来看overlay 。以IMX51 为例子,当IPU 向内核申请FB 的时候它会申请3 个FB ,一个是主屏的,还一个是副屏的,还一个就是Overlay 的。 简单地来说,Overlay就是我们将硬件所能接受的格式数据和控制信息送到这个Overlay FrameBuffer,由硬件驱动来负责merge Overlay buffer和主屏buffer中的内容。
一般来说现在的硬件都只支持一个Overlay,主要用在视频播放以及camera preview上,因为视频内容的不断变化用硬件Merge比用软件Merge要有效率得多,下面就是使用Overlay和不使用Overlay的过程:
对比图
SurfaceFlinger
surfaceFlinger 只是负责 merge Surface 的控制,比如说计算出两个 Surface 重叠的区域,至于 Surface 需要显示的内容,则通过 skia,opengl 和 pixflinger 来计算。
创建过程
在IBinder 左边的就是客户端部分,也就是需要窗口显示的应用程序,而右边就是我们的 Surface Flinger service 。 创建一个surface 分为两个过程,一个是在 SurfaceFlinger 这边为每个应用程序(Client) 创建一个管理结构,另一个就是创建存储内容的buffer ,以及在这个buffer 上的一系列画图之类的操作。
-
创建Client
因为SurfaceFlinger 要管理多个应用程序的多个窗口界面,为了进行管理它提供了一个Client 类,每个来请求服务的应用程序就对应了一个 Client 。因为 surface 是在 SurfaceFlinger 创建的,必须返回一个结构让应用程序知道自己申请的 surface 信息,因此 SurfaceFlinger 将 Client 创建的控制结构per_client_cblk_t 经过 BClient 的封装以后返回给 SurfaceComposerClient ,并向应用程序提供了一组创建和销毁 surface 的接口:
Client、BClient 与 SurfaceFlinger
Flinger 为每个 Client 提供了 8M 的空间,包括控制信息和存储内容的 buffer 。
为应用程序创建一个 Client 以后,下面需要做的就是为这个 Client 分配 Surface , 可以理解为创建一个 Surface 就是创建一个 Layer 。 -
创建 Layer
创建 Layer 的过程,首先是由这个应用程序的 Client 根据应用程序的 pid 生成一个唯一的 layer ID ,然后根据大小、位置、格式等信息创建出 Layer 。在 Layer 里面有一个嵌套的 Surface 类,它主要包含一个 ISurfaceFlingerClient::Surface_data_t ,包含了这个 Surace 的统一标识符以及 buffer 信息等,提供给应用程序使用。最后应用程序会根据返回来的 ISurface 创建一个自己的 Surface 。
Layer 创建过程
Android 提供了 4 种类型的 layer 供选择: Layer , LayerBlur , LayerBuffer , LayerDim ,每个 layer 对应一种类型的窗口,并对应这种窗口相应的操作
- Normal Layer
它是 Android 种使用最多的一种 Layer ,一般的应用程序在创建 surface 的时候都是采用的这样的 layer , Normal Layer 为每个 Surface 分配两个 buffer : front buffer 和 back buffer , Front buffer 用于 SurfaceFlinger 进行显示,而 Back buffer 用于应用程序进行画图,当 Back buffer 填满数据 (dirty) 以后,就会 flip , back buffer 就变成了 front buffer 用于显示,而 front buffer 就变成了 back buffer 用来画图。 - LayerBuffer
最复杂的一个 layer,它不具备 render buffer ,主要用在 camera preview / video playback 上。它提供了两种实现方式,一种就是 post buffer ,另外一种就是我们前面提到的 overlay , Overlay 的接口实际上就是在这个 layer 上实现的。不管是 overlay 还是 post buffer 都是指这个 layer 的数据来源自其他地方,只是 post buffer 是通过软件的方式最后还是将这个 layer merge 主的 FB ,而 overlay 则是通过硬件 merge 的方式来实现。与这个 layer 紧密联系在一起的是 ISurface 这个接口,通过它来注册数据来源。用法如下:
// 要使用 Surfaceflinger 的服务必须先创建一个 client
sp<SurfaceComposerClient> client = new SurfaceComposerClient();
// 然后向 Surfaceflinger 申请一个 Surface , surface 类型为 PushBuffers
sp<Surface> surface = client->createSurface(getpid(), 0, 320, 240,
PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers);
// 然后取得 ISurface 这个接口, getISurface() 这个函数的调用时具有权限限制的,
// 必须在Surface.h 中打开: /framewoks/base/include/ui/Surface.h
sp<ISurface> isurface = Test::getISurface(surface);
//overlay 方式下就创建 overlay ,然后就可以使用 overlay 的接口了
sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565);
sp<Overlay> verlay = new Overlay(ref);
//post buffer 方式下,首先要创建一个 buffer ,然后将 buffer 注册到 ISurface 上
ISurface::BufferHeap buffers(w, h, w, h,
PIXEL_FORMAT_YCbCr_420_SP,
transform,
0,
mHardware->getPreviewHeap());
mSurface->registerBuffers(buffers);
- 、应用程序对窗口的控制以及画图
首先了解一下 SurfaceFlinger 这个服务的运作方式:
SurfaceFlinger 是一个线程类,它继承了 Thread 类。当创建 SurfaceFlinger 这个服务的时候会启动一个 SurfaceFlinger 监听线程,这个线程会一直等待事件的发生,比如说需要进行 sruface flip ,或者说窗口位置大小发生了变化等,一旦产生这些事件,SurfaceComposerClient 就会通过 IBinder 发出信号,这个线程就会结束等待处理这些事件,处理完成以后会继续等待,如此循环。
SurfaceComposerClient 和 SurfaceFlinger 是通过 SurfaceFlingerSynchro 这个类来同步信号的,其实说穿了就是一个条件变量。监听线程等待条件的值一旦变成 OPEN 就结束等待并将条件置成 CLOSE 然后进行事件处理,处理完成以后再继续等待条件的值变成 OPEN ,而 Client 的Surface 一旦改变就通过 IBinder 通知 SurfaceFlinger 将条件变量的值变成 OPEN ,并唤醒等待的线程,这样就通过线程类和条件变量实现了一个动态处理机制。
- 在对 Surface 进行画图之前必须锁定 Surface 的 layer ,实际上就是锁定了 Layer_cblk_t 里的 swapstate 这个变量。SurfaceComposerClient 通过调用 lockSurface() 来锁定 swapsate 的值来确定要使用哪个 buffer 画图,如果 swapstate 是下面的值就会阻塞 Client ,
| value | usages |
| ------ | ------ |
| eNextFlipPending | we've used both buffers already, so we need to wait for one to become availlable. |
| eResizeRequested | the buffer we're going to acquire is being resized. Block until it is done. |
| eFlipRequested && eBusy: | he buffer we're going to acquire is currently in use by the server. |
| eInvalidSurface | this is a special case, we don't block in this case, we just return an error. | - 调用 unlockSurfaceAndPost() 来通知 SurfaceFlinger 进行Flip。或者仅仅调用 unlockSurface() 而不通知 SurfaceFlinger 。
一般来说画图的过程需要重绘 Surface 上的所有像素,因为一般情况下显示过后的像素是不做保存的,不过也可以通过设定来保存一些像素,而只绘制部分像素,这里就涉及到像素的拷贝了,需要将Front buffer 的内容拷贝到 Back buffer 。在 SurfaceFlinger 服务实现中像素的拷贝是经常需要进行的操作,而且还可能涉及拷贝过程的转换,比如说屏幕的旋转,翻转等一系列操作。因此 Android 提供了拷贝像素的 hal ,这个也可能是我们将来需要实现的,因为用硬件完成像素的拷贝,以及拷贝过程中可能的矩阵变换等操作,比用 memcpy 要有效率而且节省资源。这个 HAL 头文件在:/hardware/libhardware/hardware/include/copybit.h
窗口状态变化的处理是一个很复杂的过程,SurfaceFlinger 只是执行 Windows Manager 的指令,由 Windows manager 来决定什么是偶改变大小、位置、透明度、以及如何调整layer 之间的顺序, SurfaceFlinger 仅仅只是执行它的指令。
- SurfaceFlinger 的处理过程
监听的处理过程
前面已经说过了SurfaceFlinger 这个服务在创建的时候会启动一个监听的线程,这个线程负责每次窗口更新时候的处理。
Android 组合各个窗口的原理:Android 实际上是通过计算每一个窗口的可见区域,就是我们在屏幕上可见的窗口区域 ( 用 Android 的词汇来说就是 visibleRegionScreen ) ,然后将各个窗口的可见区域画到一个主 layer 的相应部分,最后就拼接成了一个完整的屏幕,然后将主 layer 输送到 FB 显示。在将各个窗口可见区域画到主 layer 过程中涉及到一个硬件实现和一个软件实现的问题,如果是软件实现则通过 Opengl 重新画图,其中还包括存在透明度的 alpha 计算;如果实现了 copybit hal 的话,可以直接将窗口的这部分数据直接拷贝过来,并完成可能的旋转,翻转,以及 alhpa计算等。
- handleConsoleEvent
当接收到 signal 或者 singalEvent 事件以后,线程就停止等待开始对 Client 的请求进行处理,第一个步骤是 handleConsoleEvent ,它会取得屏幕或者释放屏幕,只有取得屏幕的时候才能够在屏幕上画图。 - handleTransaction
因为窗口状态的改变只能在一个 Transaction 中进行。而窗口状态的改变可能造成本窗口和其他窗口的可见区域变化,所以就必须重新来计算窗口的可见区域。在这个处理子过程中 Android会根据标志位来对所有 layer 进行遍历,一旦发现哪个窗口的状态发生了变化就设置标志位以在将来重新计算这个窗口的可见区域。在完成所有子 layer 的遍历以后, Android 还会根据标志位来处理主layer ,举个例子,比如说传感器感应到手机横过来了,会将窗口横向显示,此时就要重新设置主 layer 的方向。 - handlePageFlip
处理每个窗口 surface buffer 之间的翻转,根据 layer_state_t 的 swapsate 来决定是否要翻转,当 swapsate 的值是 eNextFlipPending 是就会翻转。处理完翻转以后它会重新计算每个 layer的可见区域。 - handleRepaint
计算出每个 layer 的可见区域以后,这一步就是将所有可见区域的内容画到主 layer 的相应部分了,也就是说将各个 surface buffer 里面相应的内容拷贝到主 layer 相应的 buffer ,其中可能还涉及到alpha 运算,像素的翻转,旋转等等操作,这里就像我前面说的可以用硬件来实现也可以用软件来实现。在使用软件的 opengl 做计算的过程中还会用到 PixFlinger 来做像素的合成, - postFrameBuffer
翻转主 layer 的两个 buffer ,将刚刚写入的内容放入 FB 内显示了。
共享内存
普通的Android控件,例如TextView、Button和CheckBox等,它们都是将自己的UI绘制在宿主窗口的绘图表面之上,这意味着它们的UI是在应用程序的主线程中进行绘制的。由于应用程序的主线程除了要绘制UI之外,还需要及时地响应用户输入,否则系统就会认为应用程序没有响应了。而对于一些游戏画面,或者摄像头预览、视频播放来说,它们的UI都比较复杂,而且要求能够进行高效的绘制。这时候就必须要给那些需要复杂而高效UI的视图生成一个独立的绘图表面,以及使用一个独立的线程来绘制这些视图的UI。
SurfaceFlinger服务运行在Android系统的System进程中,它负责管理Android系统的帧缓冲区(Frame Buffer)。Android应用程序为了能够将自己的UI绘制在系统的帧缓冲区上,它们就必须要与SurfaceFlinger服务进行通信。
在APP端执行draw的时候,数据很明显是要绘制到APP的进程空间,但是视图窗口要经过SurfaceFlinger图层混排才会生成最终的帧,而SurfaceFlinger又运行在另一个独立的服务进程,那么View视图的数据是如何在两个进程间传递的呢,普通的Binder通信肯定不行,因为Binder不太适合这种数据量较大的通信,那么View数据的通信采用的是什么IPC手段呢?答案就是共享内存,更精确的说是匿名共享内存。共享内存是Linux自带的一种IPC机制,Android直接使用了该模型,不过做出了自己的改进,进而形成了Android的匿名共享内存(Anonymous Shared Memory-Ashmem)。通过Ashmem,APP进程同SurfaceFlinger共用一块内存,如此,就不需要进行数据拷贝,APP端绘制完毕,通知SurfaceFlinger端合成,再输出到硬件进行显示即可。
View绘制与共享内存
在每一个Android应用程序与SurfaceFlinger服务之间的连接上加上一块用来传递UI元数据的匿名共享内存,这个共享内存就是 SharedClient
shareClient.jpg
在每一个SharedClient里面,有至多31个SharedBufferStack。SharedBufferStack就是Android应用程序和SurfaceFlinger 的缓冲区堆栈。用来缓冲 UI 元数据。
一般我们就绘制UI的时候,都会采用一种称为“双缓冲”的技术。双缓冲意味着要使用两个缓冲区,其中一个称为Front Buffer,另外一个称为Back Buffer。UI总是先在Back Buffer中绘制,然后再和Front Buffer交换,渲染到显示设备中。这下就可以理解SharedBufferStack的含义了吧?SurfaceFlinger服务只不过是将传统的“双缓冲”技术升华和抽象为了一个SharedBufferStack。可别小看了这个升华和抽象,有了SharedBufferStack之后,SurfaceFlinger 服务就可以使用N个缓冲区技术来绘制UI了。N值的取值范围为2到16。例如,在Android 2.3中,N的值等于2,而在Android 4.1中,据说就等于3了。
在SurfaceFlinger服务中,每一个SharedBufferStack都对应一个Surface,即一个窗口。这样,我们就可以知道为什么每一个SharedClient里面包含的是一系列SharedBufferStack而不是单个SharedBufferStack:一个SharedClient对应一个Android应用程序,而一个Android应用程序可能包含有多个窗口,即Surface。从这里也可以看出,一个Android应用程序至多可以包含31个Surface。
我们假设图中的SharedBufferStack有5个Buffer,其中,Buffer-1和Buffer-2是已经使用了的,而Buffer-3、Buffer-4和Buffer-5是空闲的。指针head和tail分别指向空闲缓冲区列表的头部和尾部,而指针queue_head指向已经使用了的缓冲区列表的头部。从这里就可以看出,从指针tail到head之间的Buffer即为空闲缓冲区表,而从指针head到queue_head之间的Buffer即为已经使用了的缓冲区列表。注意,图中的5个Buffer是循环使用的。
SharedBufferStack中的缓冲区只是用来描述UI元数据的,这意味着它们不包含真正的UI数据。真正的UI数据保存在GraphicBuffer中,后面我们再描述GaphicBuffer。因此,为了完整地描述一个UI,SharedBufferStack中的每一个已经使用了的缓冲区都对应有一个GraphicBuffer,用来描述真正的UI数据。当SurfaceFlinger服务缓制Buffer-1和Buffer-2的时候,就会找到与它们所对应的GraphicBuffer,这样就可以将对应的UI绘制出来了。
当Android应用程序需要更新一个Surface的时候,它就会找到与它所对应的SharedBufferStack,并且从它的空闲缓冲区列表的尾部取出一个空闲的Buffer。我们假设这个取出来的空闲Buffer的编号为index。接下来Android应用程序就请求SurfaceFlinger服务为这个编号为index的Buffer分配一个图形缓冲区GraphicBuffer。
SurfaceFlinger 服务分配好图形缓冲区 GraphicBuffer 之后,会将它的编号设置为 index,然后再将这个图形缓冲区 GraphicBuffer 返回给 Android 应用程序访问。Android应用程序得到了 SurfaceFlinger 服务返回的图形缓冲区 GraphicBuffer 之后,就在里面写入UI数据。写完之后,就将与它所对应的缓冲区,即编号为 index 的 Buffer,插入到对应的 SharedBufferStack 的已经使用了的缓冲区列表的头部去。这一步完成了之后,Android 应用程序就通知 SurfaceFlinger 服务去绘制那些保存在已经使用了的缓冲区所描述的图形缓冲区GraphicBuffer了。用上面例子来说,SurfaceFlinger服务需要绘制的是编号为1和2的Buffer所对应的图形缓冲区GraphicBuffer。由于SurfaceFlinger服务知道编号为1和2的 Buffer 所对应的图形缓冲区 GraphicBuffer 在哪里,因此,Android 应用程序只需要告诉 SurfaceFlinger 服务要绘制的 Buffer 的编号就OK了。当一个已经被使用了的Buffer被绘制了之后,它就重新变成一个空闲的 Buffer 了。
SharedBufferStack 是在 Android 应用程序和 SurfaceFlinger 服务之间共享的,但是,Android 应用程序和 SurfaceFlinger 服务使用 SharedBufferStack 的方式是不一样的,具体来说,就是 Android 应用程序关心的是它里面的空闲缓冲区列表,而 SurfaceFlinger 服务关心的是它里面的已经使用了的缓冲区列表。从SurfaceFlinger服务的角度来看,保存在 SharedBufferStack中 的已经使用了的缓冲区其实就是在排队等待渲染。
为了方便 SharedBufferStack 在 Android 应用程序和 SurfaceFlinger 服务中的访问,Android 系统分别使用 SharedBufferClient 和 SharedBufferServer 来描述 SharedBufferStack ,其中,SharedBufferClient 用来在Android 应用程序这一侧访问 SharedBufferStack 的空闲缓冲区列表,而 SharedBufferServer 用来在SurfaceFlinger 服务这一侧访问 SharedBufferStack 的排队缓冲区列表。
SharedBufferClient眼中的SharedBufferStack
只要 SharedBufferStack 中的 available 的 buffer 的数量大于0, SharedBufferClient 就会将指针 tail 往前移一步,并且减少 available 的值,以便可以获得一个空闲的 Buffer。当 Android 应用程序往这个空闲的 Buffer 写入好数据之后,它就会通过 SharedBufferClient 来将它添加到 SharedBufferStack 中的排队缓冲区列表的尾部去,即指针 queue_head 的下一个位置上。
SharedBufferServer眼中的SharedBufferStack.jpg
当 Android 应用程序通知 SurfaceFlinger 服务更新UI的时候,只要对应的 SharedBufferStack 中的 queued 的缓冲区的数量大于0,SharedBufferServer 就会将指针 head 的下一个Buffer绘制出来,并且将指针 head 向前移一步,以及将 queued 的值减1。
参考:
https://blog.csdn.net/luoshengyang/article/details/7846923