本文介绍了Android中SurfaceFlinger的工作原理,对大家的学习或者工作有一定的参考价值。有需要的朋友下面和边肖一起学习。
概念
SurfaceFlinger是一个系统服务,比如audioflinger、audiopolicyservice等。通过本文了解系统的主要服务,Android的系统服务一目了然。该系统服务主要实现Surface的建立、控制、管理等功能。换句话说,在Android的实现中,它是一个服务,提供全系统的surface composer功能,可以将各种应用的2D和3D surface结合起来。
原理分析
我们先来看看下面这张屏幕草图:
每个应用程序可以对应一个或多个图形界面,每个界面称为一个表面或窗口。上图我们可以看到四个面,一个是home界面,一个是红绿蓝代表的三个面,而两个按钮其实就是home面的内容。在这里,我们可以看到图形显示需要解决的问题:
a、首先每个面在屏幕上都有它的位置和大小,然后每个面都有要显示的内容。当我们改变应用程序时,这些元素,如内容、大小和位置可能会改变。当我们改变的时候应该怎么做?
b,那么各个表面之间可能有重叠。比如上面的素描,绿色覆盖蓝色,红色覆盖绿蓝和下面的家,它也有一定的透明性。应该如何描述这些层之间的关系?
首先我们来看第二个问题。我们可以想象在屏幕平面的垂直方向还有一个Z轴。所有的曲面都是根据Z轴上的坐标确定前后的,这样我们就可以描述曲面之间的上下覆盖关系。Z轴上的这个顺序有一个专业术语叫做图上的Z顺序。
对于第一个问题,我们需要一个结构来记录应用界面的位置和大小,需要一个缓冲区来记录要显示的内容,所以这就是我们surface的概念。其实我们可以把它理解为一个容器,它记录了应用程序界面的控制信息,比如大小,位置,它还有一个缓冲区,用来存储要显示的内容。
这里还有一个问题,就是有图形重合的情况下如何处理,可能会有一些信息透明的面。这就是我们SurfaceFlinger需要解决的问题。它要将各个曲面组合(composite/merge)成一个主曲面,最后将主曲面的内容发送到FB/V4l2输出,这样我们就可以在屏幕上看到想要的效果。其实这些曲面的融合有两种方式,一种是以软件的形式融合,一种是以硬件的形式融合。软件方式是我们的SurfaceFlinger,硬件方式是Overlay。
OverLay
因为硬件合并的内容比较简单,所以我们先来看看overlay。Overlay可以通过多种方式实现,但都需要硬件支持。以IMX51为例。IPU在向内核申请FB的时候,会申请三个FB,一个主屏,一个副屏,一个叠加。简单来说,叠加就是我们把硬件可以接受的格式数据和控制信息发送到这个叠加帧缓冲区,硬件驱动负责合并叠加缓冲区和主屏缓冲区中的内容。
一般来说,目前的硬件只支持一个叠加,主要用于视频播放和相机预览。因为视频内容是不断变化的,所以使用硬件合并比软件合并效率要高得多。下面是使用和不使用叠加的过程:
叠加hal被添加到SurfaceFlinger。只要认识到这个叠加hal,就可以使用叠加的功能。此头文件位于:
/hardware/lib hardware/include/harware/overlay . h
可以通过FB或者V4L2输出来实现,这可能是我们以后工作的内容。实施覆盖hal后,使用覆盖接口的顺序如下:
/frameworks/base/libs/surface flinger/tests/overlays/overlays . CPP,
这个序列非常重要,我们将在后面讨论。
然而,在实践中,我们不一定需要实现Overlay hal。如果我们知道硬件,就可以直接把这些信息发送到驱动中的Overlay Buffer,而不用走上层的Android。l FSS目前的相机预览就是这样采用的,我粗略看过r3补丁的内容,应该也是在opencore的视频播放部分实现的。
SurfaceFlinger
现在让我们来看看最复杂的SurfaceFlinger。首先要明确的是,SurfaceFlinger只负责merge曲面的控制,比如计算两个曲面的重叠面积。至于表面需要显示的内容,由skia、opengl和pixflinger计算。所以在引入SurfaceFlinger之前,我们先忽略它里面存储了什么,先搞清楚merge的一系列控制过程,再结合2D和3D引擎来看它的处理过程。
3.1、Surface的创建过程如前所述,每个应用程序可能有一个或多个表面。我们需要一些数据结构来存储我们的窗口信息,我们还需要缓冲区来存储我们的窗口内容。更重要的是,我们应该确定一个方案来与SurfaceFlinger就这些信息进行交互。我们先来看下面这个曲面创建过程的类图:
IBinder左边是客户端部分,也就是需要窗口显示的应用,右边是我们的Surface Flinger服务。创建一个surface分为两个过程,一个是在SurfaceFlinger端为每个应用(客户端)创建一个管理结构,另一个是创建一个存储内容的缓冲区,以及在这个缓冲区上绘图等一系列操作。
因为SurfaceFlinger要管理多个应用的多个窗口接口,所以提供了一个客户端类进行管理,每个请求服务的应用对应一个客户端。因为表面是在SurfaceFlinger中创建的,所以需要返回一个结构,让应用程序知道应用表面的信息。因此,SurfaceFlinger将客户端创建的控制结构per _ Client _ cblk _ t经过BClient封装后返回给SurfaceComposerClient,并为应用程序提供了一套创建和销毁表面的操作:
在为应用程序创建客户机之后,接下来需要做的事情是为这个客户机分配表面。Flinger为每个客户端提供8M的空间,包括控制信息和存储内容的缓冲区。在创建曲面之前,我们应该先了解层的概念。回到我们之前看到的屏幕草图,其实每个窗口都是Z轴上的一层。layer提供了窗口控制信息的操作和内容的处理(调用opengl或者skia),也就是说SurfaceFlinger只控制处理这些信息的时间和过程。所有的实际处理都是在层中完成的,这可以理解为创建一个表面意味着创建一个层。不得不说Android这些乱七八糟的名字让我纠结了很久.
在创建图层的过程中,这个应用的客户端首先根据应用的pid生成一个唯一的图层ID,然后根据大小、位置、格式等信息创建图层。Layer中有一个嵌套的Surface类,主要包含一个iSurfaceFlingerClient:Surface _ Data _ T,包含这个表面的统一标识符和缓冲区信息等。是为应用程序提供的。最后,应用程序将根据返回的ISurface信息创建自己的表面。
Android提供了四种类型的层可供选择,每一层对应一种类型的窗口,并对应该窗口的相应操作:
层,层缓冲,层缓冲,层缓冲.
LayerBuffer可以很容易理解为Layer的缓冲区,其实就是一个图层类型。各层的效果可以参考surface . Java:/Frameworks/base/core/Java/Android/view/surface . Java中的描述,这里我们将重点介绍两种层,一种是层(norm层),另一种是LayerBuffer。
Norm层是Android中应用最广泛的层,一般应用在创建surface时都会用到。了解了正常层,可以让我们了解Android在显示过程中的一些基本原理。正常层为每个表面分配两个缓冲区:前缓冲区和后缓冲区。这个前后是相对的概念,可以翻转。前缓冲区用于surfaceFlinger显示,后缓冲区用于应用程序绘制。当后台缓冲区充满脏数据时,它会翻转,后台缓冲区成为显示的前台缓冲区,而前台缓冲区成为绘图的后台缓冲区。这两个缓冲区的大小根据表面的大小和格式动态变化。我没有仔细看这个动态变化的实现。可以参考:/frameworks/base/lib/surface flinger/layer . CPP中的setbuffers()。
两个缓冲区翻转的方法是Android display中一个重要的实现方法,不仅针对每个面,也针对最终写入FB的主面。
LayerBuffer也是以后肯定会用到的一层,我个人认为也是最复杂的一层。它没有渲染缓冲,主要用于相机预览/视频播放。它提供了两种实现,一种是post buffer,另一种是overlay,我们前面提到过。Overlay的接口实际上就是在这一层实现的。无论是overlay还是post buffer,都是指这一层的数据来自其他地方,但是post buffer最终是通过软件合并这一层的FB,而overlay是通过硬件合并实现的。与这一层紧密相连的是接口ISurface,通过它可以注册数据源。下面举一个例子来说明这两种方式的用法:
最初的几个步骤是一般性的:
//要使用Surfaceflinger的服务,必须先创建一个客户端。
sp client=new surface composerclient();
//然后应用于表面的Surfaceflinger,该表面的类型为PushBuffers
sp surface=client-create surface(getpid(),0,320,240,PIXEL_FORMAT_UNKNOWN,ISurfaceComposer:ePushBuffers);
//然后获取接口ISurface。如果在权限限制下调用getISurface()函数,则必须在surface . h:/frame woks/base/include/ui/surface . h中打开。
SPI surface=Test:geti surface(surface);
//在叠加模式下创建叠加,然后就可以使用叠加的接口了。
spref=isurface-create overlay(320,240,PIXEL _ FORMAT _ RGB _ 565);
sp verlay=new Overlay(ref);
//在//post buffer模式下,先创建一个缓冲区,然后在ISurface上注册缓冲区。
ISurface:BufferHeap缓冲区(w,h,w,h,
像素_格式_YCbCr_420_SP
转换,
0,
mHardware-getPreviewHeap());
mssurface-register buffers(缓冲区);
3.2、应用程序对窗口的控制和画图表面创建后,应用程序可以在缓冲区中绘制图片,这里我们面临以下问题:
A.你怎么知道该利用哪个缓冲区呢?
b,即画图后如何通知SurfaceFlinger翻转?
c,除了画图,如果我们移动窗口,改变窗口大小,我们怎么告诉SurfaceFlinger处理?
在了解这些问题之前,我们首先需要了解SurfaceFlinger这种服务是如何工作的:
从类图中可以看出,SurfaceFlinger是一个Thread类,它继承了Thread类。在创建服务SurfaceFlinger的时候,会启动一个SurfaceFlinger监听器线程,这个线程会一直等待事件的发生,比如需要sruface翻转,或者窗口位置大小的变化等等。一旦生成这些事件,SurfaceComposerClient就会通过IBinder发送一个信号,这个线程就会结束对这些事件的等待,在处理完成后会继续等待,以此类推。
SurfaceComposerClient和SurfaceFlinger通过类SurfaceFlingerSynchro同步信号,这个类实际上是一个条件变量。线程等待条件的值变为开放。一旦它打开,它就结束等待,并将条件设置为关闭,然后处理事件。处理完成后,它继续等待条件的值变为OPEN。一旦客户端的表面发生变化,就通知SurfaceFlinger更改条件变量的值通过IBinder打开,并唤醒等待的线程。因此,通过线程类和条件变量实现了动态处理机制。
知道了SurfaceFlinger的事件机制,我们再回头看看前面提到的问题。首先,在绘制表面之前,必须锁定表面的图层,实际上是锁定layer _ cblk _ t中的swapstate变量,SurfaceComposerClient通过swap state的值来确定使用哪个缓冲区进行绘制。如果swapstate是下面的值,它将阻塞客户端,所以它将直接复制而不进行转换:
//如果出现以下情况,我们会阻止客户端:
//eNextFlipPending:我们已经使用了两个缓冲区,所以我们需要
//等待其中一个变为可用。
//eResizeRequested:我们要获取的缓冲区正在
//调整大小。阻塞,直到它完成。
//eFlipRequested eBusy:我们要获取的缓冲区是
//当前正被服务器使用。
//eInvalidSurface:这是一个特例,我们不在此阻塞
//case,我们只是返回一个错误。
因此,应用程序首先调用lockSurface()来锁定层的swapstate,并获取用于绘制的缓冲区,然后它可以在其上进行绘制。完成后,它会调用unlockSurfaceAndPost()通知SurfaceFlinger翻转。或者直接调用unlockSurface()而不通知SurfaceFlinger。
一般来说,绘制的过程需要重新绘制表面上的所有像素,因为显示的像素一般不会被保存。但是通过设置可以保存一些像素,只画一些像素。这里涉及到像素的复制,需要将前台缓冲区的内容复制到后台缓冲区。在SurfaceFlinger服务的实现中,像素复制是一个比较频繁的操作,可能还会涉及到复制过程的转换,比如屏幕旋转、翻转等一系列操作。所以Android提供了复制像素的hal,而这可能是我们未来需要实现的东西,因为在复制过程中使用硬件完成像素复制和可能的矩阵变换,比使用memcpy更高效,更节省资源。这个HAL头文件位于:
/hardware/lib hardware/hardware/include/copy bit . h
窗口状态变化的处理是一个非常复杂的过程。首先要说明的是,SurfaceFlinger只是执行了Windowsmanager的指令,决定了什么甚至改变了大小、位置、透明度以及如何调整层间顺序。SurfaceFlinger只是执行它的指令。PS: Windows Manager是java层的一个服务,为所有的窗口提供管理功能。这部分我没有详细看过,我觉得是以后需要了解的。
窗口状态的变化包括位置、窗口大小、透明度、z顺序等。首先,我们来了解一下SurfaceComposerClient是如何与SurfaceFlinger交互的。当应用程序需要改变窗口的状态时,它会将所有的状态改变信息打包在一起发送给SurfaceFlinger。SurfaceFlinger改变这些状态信息后,会唤醒等待的监控线程,设置一个标志位,告诉监控线程窗口的状态已经改变,必须进行处理。在Android的实现中,这个打包过程是一个事务,所有对窗口状态(layer_state_t)的改变都必须在一个事务中。
这里已经完成了应用客户端的处理过程,基本上分为两个部分,一个是窗口内的绘制,一个是窗口状态变化的处理。
4、SurfaceFlinger的处理过程
了解了Flinger和客户端的交互,我们再来仔细看看SurfaceFlinger的处理过程。前面已经说过了,SurfaceFlinger的这个服务在创建的时候会启动一个监控线程,这个线程负责每次窗口更新的处理。让我们仔细看看这个线程对事件的处理,大致如下图:
先说一下Android中合并窗口的原理:Android实际上是计算每个窗口的可视面积,也就是我们在屏幕上能看到的窗口面积(在Android词汇中,visibleRegionScreen),然后将每个窗口的可视面积绘制到一个主层的对应部分,最后拼接成一个完整的屏幕,再将主层发送给FB进行显示。将每个窗口的可视区域绘制到主层的过程包括硬件实现和软件实现。如果是软件实现,会用Opengl重画,包括透明的alpha计算。如果实现copybit hal,可以直接复制窗口的这部分数据,完成alhpa可能的旋转、翻转、计算。
我们来看看Android把各层结合起来,发送给FB显示的具体流程:
4.1、handleConsoleEvent收到信号或singalEvent事件后,线程停止等待,开始处理客户端的请求。第一步是handleConsoleEvent,我读到的是与device /dev/console相关的。它会得到屏幕或者释放屏幕,只有得到屏幕才能在屏幕上画画。
4.2、handleTransaction如前所述,窗口状态只能在一个事务中更改。因为窗口状态的改变可能会导致这个窗口和其他窗口的可视区域发生变化,所以需要重新计算窗口的可视区域。在这个子过程中,Android会根据标志位遍历所有层,一旦发现哪个窗口的状态发生了变化,就会设置标志位,重新计算这个窗口未来的可见区域。遍历完所有子层后,Android还会根据标志位处理主层。比如传感器感应到手机过来,就会横向显示窗口,所以需要重新设置主层的方向。
4.3、handlePageFlip这里将处理每个窗口的表面缓冲区之间的翻转。是否翻转会根据layer _ state _ t的swapsate来决定,当swapsate的值为eNextFlipPending yes时,就会翻转。处理翻转后,它将重新计算每层的可见区域。重新计算的过程我还不是很懂,但大致是这样一个过程:
从具有最大Z值的层开始,也就是说,从顶层开始,通过移除其自身的透明区域和覆盖其的不透明区域来获得该层的可见区域。然后这一层的不透明区域会累加到不透明覆盖区域,这一层的可见区域会放入主层的可见区域,然后计算下一层,直到计算完所有层的可见区域。中间的计算是通过skia中定义的一种AND-OR图形逻辑运算来实现的,类似于我们数学中的AND-OR逻辑图。
4.4、handleRepaint在计算出每一层的可视区域后,这一步就是将所有可视区域的内容绘制到主层的对应部分,也就是说将每个表面缓冲区中的对应内容复制到主层的对应缓冲区中,其中可能还涉及到alpha运算、像素翻转、旋转等操作。这里就像我前面说的,可以用硬件实现,也可以用软件实现。在用opengl软件计算的过程中,会用到PixFlinger来合成像素,我还没来得及看。
4.5、postFrameBuffer最后一个任务是翻转主层的两个缓冲区,并在FB中显示刚刚写入的内容。
这就是这篇关于SurfaceFlinger在Android中如何工作的文章。希望对大家的学习有帮助,也希望大家多多支持。