Android GUI系统 SurfaceFlinger 深入理解android内核设计思想 第九章 笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012654756/article/details/89708085

第九章,GUI系统-surfaceFlinger

流畅性,友好性,可拓展性

9.1,openGL ES,EGL

SurfaceFlinger是GUI系统的核心,但是对于GLES,只是一个APP

整体框架:

由底层到上层顺序分析:

1,Linux内核提供统一的framebuffer显示驱动。

在/dev/graphics/fb,或者/dev/fb,fb0表示第一个monitor。

2,android HAL层提供Gralloc,包括fb和gralloc,

fb负责打开内核中的framebuffer,初始化配置,提供post,setSwapInterval等操作接口。

gralloc负责framebuffer的分配和释放

上层只能通过gralloc间接访问framebuffer,保证framebuffer的有序使用和统一管理

HAL层还有一个composer,它为厂商自定制UI合成提供接口。

composer直接使用者是surfaceFlinger中的HWComposer(有两个),HWComposer除了管理composer,还负责VSync信号的产生和控制。

VSync是Project Butter工程加入的同步机制,可由硬件产生或软件模拟(VsyncThread)

3,OpenGLES是一个通用函数库,在不同平台需要被本地化,与平台的窗口系统建立联系才能正常工作。

framebufferNativeWindow是负责GLES在android本地化的中介之一。

后面还会看到android APP使用的另一个本地窗口

4,OpenGL更多知识一个接口协议,具体实现可以用软件,也可依托于硬件。

这样给产品开发带来了灵活性,那么GLES在动态运行时是如何取舍的?这也是EGL的作用之一,它会去读取egl.cfg这个配置文件,然后根据用户的设定来动态加载libagl(软件实现)或者libhgl(硬件实现)。

5,SurfaceFlinger持有一个成员数组mDisplays描述系统中支持的各种显示设备,具体有哪些Display是由SurfaceFlinger在readyToRun中判断并赋值的。DisplayDevice在初始化时还将调用eglGetDisplay,eglCreateWindowSurface等接口,并利用EGL完成对OpenGLES环境的搭建。

6,很多模块都可以调用OpenGLES提供的API(这些接口以gl为前缀,如glViewport,glClear,glMatrixMode,glLoadIdentity等),包括SurfaceFlinger,DisplayDevice等。

7,与OpenGLES相关的模块:

配置类:帮助GLES完成配置的,EGL,DisplayHardware等

依赖类:GLES运行依赖的本地化的东西,如FramebufferNativeWindow

使用类:使用者也可能是配置者,DisplayDevice构建了GLES环境,也是GLES的用户。

9.2,Android的硬件接口:HAL

对于android很多子系统(显示系统,音频系统),HAL非常重要,

HAL是子系统与LinuxKernel驱动之间通信的统一接口。

HAL需要解决如下问题:

硬件接口抽象:HAL不是针对某个硬件设备,而是从众多设备提取它们的共同属性并付诸软件实现

接口的稳定性:HAL层的接口不允许频繁改动,否则失去意义。

灵活的使用方法:以满足开发商和上层使用者定制需求。

下面围绕这3个关键点讨论:

1,硬件接口抽象:

抽象涉及继承关系,抽象的硬件是父类,具体的硬件是子类,C++中很容易实现,但HAL多是C语言实现,怎么办?

只要让子类数据结构的第一个成员是父类接口就可以。。如Gralloc(后面会详细分析):

/hardware/libhardware/include/hardware/Gralloc.h

每个hardware module都有一个数据接口叫HAL_MODULE_INFO_SYM

而且这个数据结构必须以hw_module_t作为第一个元素,后面才是特有结构。

这样就有gralloc_module_t继承自hw_module_t的效果

2,接口的稳定性:

对某一类硬件设备,它提供的HAL接口必须是稳定不变的,android系统已经预先定义好了接口,硬件设备商填空即可。接口统一定义在:

/hardware/libhardware/include/hardware:hwcomposer.h

Gralloc硬件接口定义:

typedef struct gralloc_module_t{

struct hw_module_t common;

int (*registerBuffer)();...}

后续会对上述重要接口讨论、

3,灵活的使用方法:

硬件厂商:只需要实现HAL接口,上层问题和android版本适配问题不用考虑。

Android手机开发商:只需要移植HAL就可以控制硬件,

9.3,Android终端显示设备的化身:Gralloc,FrameBuffer

做过Linux开发的人对Framebuffer不会太陌生,它是内核系统提供的图形硬件的抽象描述。

称之为 buffer,是因为它也占用了系统存储空间的一部分,是一块包含屏幕显示信息的缓冲区。

所以在一切都是文件的Linux系统中,Framebuffer被看成了终端显示设备的化身。

android系统中,framebuffer提供的设备文件节点是/dev/graphics/fb0,android各个子系统不会直接使用内核驱动,而是HAL层间接引用底层架构。现实系统也是如此,它借助于HAL层操作framebuffer,完成这一中介人物的就是Gralloc。下面分几个方面介绍:

1,Gralloc模块的加载

Gralloc对应的模块由FramebufferNativeWindow(GLES的本地窗口之一)在构造函数中加载,:

hw_get_module(GRALLOC_HARDWARE_MODULE_ID,&module);

函数hw_get_module是上层使用者加载HAL库的入口,Gralloc的硬件模块ID:

#define GRALOC_HARDWARE_MODULE_ID "gralloc"

hw_get_module会在下面路径查找ID匹配的库:

#define HAL_LIBARD_PATH1 "/system/lib/hw"和PATH2,/vendor/lib/hw

lib库名:gralloc.[ro.hardware].so。。。。,如果上述不存在,android有个default库,gralloc.default.so,

在hardware/libhardware/modules/gralloc/中,主要有graloc.cpp,framebuffer.cpp,mapper.cpp

2,Gralloc提供的接口

Gralloc对应的HAL库被成功加载后,下面看下它提供的重要接口:

Gralloc是hw_module_t的子类,所以必须实现hw_module_methods_t。这个数据结构里面目前只有一个函数指针变量Open,

上层调用hw_get_module是,系统先加载正确的HAL库,然后open方法打开设备。

Gralloc中,open接口可以打开2个设备:

#define GRALLOC_HARDWARE_FB0 "fb0"

#define GRALLOC_HARDWARE_GPU0 "gpu0"

fb0就是前面说的主屏幕,gpu0负责图形缓冲区的分配和释放。

两个设备分别由FramebufferNativeWindow的fbDev和grDev成员变量管理:

/frameworks/native/libs/ui/FramebufferNativeWindow.cpp

FramebufferNatrveiWindow():fbDev(0),grDev(0){

framebuffer_open(module, &fbDev)//打开fb设备

gralloc_open(module,&grDev)//打开gralloc设备

}

上面两个open函数由hardware目录下的fb.h和Gralloc.h提供,他们最终调用的还是hw_module_methods的open,只是设备名不同,分别是FB0/GPU0。

对于Android默认的Gralloc实现,open方法接口对应gralloc_device_open,它根据设备名判断打开fb还是gralloc设备。

/hardware/libhardware/modules/gralloc/Gralloc.cpp

先看framebuffer设备打开过程:

/hardware/libhardware/modules/gralloc/Frambuffer.cpp

fb_device_open():

strcmp检查设备名是否为GRALLOC_HARDWARE_FB0

分配hw_device_t空间:(fb_context_t*)malloc(sizeof(*dev))

初始化:memset(dev,0,sizeof(*dev))

下面是接口,是fb设备的核心:

dev->deivce.common.close=fb_close;

dev->deivce.setSwapInterval-fb)setSwapInterval;

dev->device.post=fb)post;

内存映射,和参数配置:mapFramebuffer(m);

壳和核心的关系:*device=&dev0>device.common;

}

其中fb_context_t是frambuffer内部使用的一个类,包含了很多信息,而函数参数device的内容分只是fb_context_t内部的device.common。

这种通用和特殊属性并存的编码风格在HAL中很常见。

数据类型fb_context_t唯一成员就是framebuffer_device_t,它是对framebuffer设备的统一描述。

一个标准的fb设备通常提供如下接口实现:

int (*post)(framebuffer_device_t,buffer_handle_t)//将buffer数据post到显示屏,buffer必须与屏幕尺寸一致,且没有被locked。buffer内容会在下一次VSYNC中显示出来。

int *setSwapInterval(),设置两个缓冲区交换的时间间隔,

Int(*setUpdateRect)(frambuffer_device_t,left,top,width,height),设置刷新区域,区域外是无效的。

下面看frambuffer_device_t中的重要成员变量:

width/height,xdpi/ydpi。format(RGBA_8888,RGBX_8888,RGB_888 , RGB_565),fps,SwapInterval。

目前还没看到系统如何打开kernel层的fb设备和如何配置的,这些都是在mapFrameBuffer中完成的。

mapFrameBuffer首先打开/dev/graphics/fb0或者/dev/fb0,

然后通过ioctl(fd, FBIOGET_FSCREENINFO,&finfo)和FBIOGERT_VSCREENINFO获取显示屏参数。

通过ioctl(fd,FBIOPUT_VSCREDINFO,&info)对fb进行配置。

mapFrambuffer另一个任务就是为fb设备做内存映射:

module->framebuffer->base=mmap(0,fbSize,RW,SHARED,fd,0)

由上可知,映射地址保存在module->framebuffer->base,变量module对应的是前面hw_get_module(GRALLOC_HARDWARE_MODULE_ID,&module)得到的hw_module_t(强制转换为private_module_t)

再看系统打开gralloc设备的过程,它相对fb简单些:

gralloc_device_open(),

也是gralloc_context_t *dev = (gralloc_context_t*)malloc(sizeof(*dev))//分配空间,

然后gralloc_context里面有framebuffer_device,

dev->device.alloic=gralloc_alloc/free,从提供接口看,gralloc主要负责framebuffer的分配和释放操作。

9.4,Android中的本地窗口

OGL学习过程不断提及NativeWindow的概念,它是OpenGL兼容多种系统的关键,根据android GUI的涉及理念,可以猜到至少要两种nativeWindow。

1,面向管理者SurfaceFlinger,

SurfaceFlinger是所有UI界面的管理者,它一定要直接或者间接的持有本地窗口,这个窗口就是FrambufferNativeWindow。

2,面向APP

这类本地窗口是surface

一个系统涉及一种本地窗口不就可以了吗?问啥要两个或者以上?完全可以用一个实现:

理想模型:OGL->NatriveWindow->Framebuffer

NativeWindow管理framebuffer,OGL就像通用打印机,NativeWindow是纸,承载OGL的输出结果。OGL不介意nativeWindow是A4或者A6纸,都只是本地窗口。

那么这种理想模型符合android需求吗?整个系统仅有一个要显示UI的程序是可以的,那有N个UI程序时会怎样?

一个系统设备只有一个Framebuffer,按照理想设计,每个APP都要各自使用和管理framebuffer,就会混乱。

改进:(APP1)OES->NativeWIndow-2 -> memoryBuffer->(SurfaceFlinger)OES->nativeWindow-1->framebuffer

nativeWindow1是能直接显示在屏幕上的,它使用了帧缓冲区;nativeWin2实际是从内存缓冲区分配的空间。

当系统有多个需要显示UI的APP时,一方面这种改进保证了每个AP都可以获得”本地窗口“,另一方面,这些“本地窗口”也能被有序显示到屏幕,因为surfaceFlinger会收集所有AP的显示需求,对他们进行统一的图像混合操作,然后输出到自己的NativeWindow1上。

直接用OES门槛比较高,AP也可以通过Skia等第三方图形库,正常情况按照SDK向导生成的APK都是这种。想使用OES完成复杂渲染的开发者,也可以用android系统封装的GLSUrefaceView来达到目标。

下面的源码分析会对改进系统做验证

9.4.1 FramebufferNativeWindow

EGL要通过本地窗口来为OES创造环境,函数:

EGLSurface eglCreateWindowSurface(dpy,config,NativeWindowType,attrib_list)

这里不同OS下NativeWIndowType不同,android是typedefine struct ANativeWIndow* EGLNativeWindowTyp,在:

/frameworks/native/opengl/oinclude/egl/eglplatform.h

ANatriveWindow定义在Window.h:

/syste/core/include/system/Window.h

struct ANativeWindow

SwapInterval;xdpi;int (*dequeueBuffer) (struct ANativeWindow *window,struct ANativeWindowBuffer** buffer , int* fenceFd).

ANativeWindow还有很多成员函数:

setSwapInterval:设置交换间隔时间

dequeueBuffer,EGL通过这个接口来申请buffer,前面说过,两个本地窗口提供的buffer分别来自于framebuffer和内存空间。dequeue是出队列,所以window包含的buffer应该不止一个。

queueBuffer,EGL对buffer渲染完成后,调用它来unlock和postBuffer。

cancelBuffer,消除一个dequeue的buffer,要注意同步问题

query,

performm:执行本地窗口支持的操作:set usage,crop,buffer_count,buffer_transofrm,buffer_timestamp等。

ANativeWindow更像是一份协议,规定了本地窗口的形态和功能,这样才能支持多种本地窗口,这样才能针对某种特定的平台窗口填充具体的实现。

这个小节看FrambufferANtiveWindow,是如何履行这个协议的:

根据FrambufferNativeWindow的功能,可以推测他的构造函数应该完成如下init操作:

加载GRALLOC_HARDWARE_MODULE_ID模块,详细流程在Gralloc小节解释过了。

分别打开fb和gralloc设备,在gralloc小节分析过,打开后的设备由全局变量fbDev和grDev管理。

根据设备的属性给FramebufferNativeWindow赋初值

根据FrambufferNativeWIndow的实现填充ANativeWIndow的协议

其他init

下面从源码角度分析上述的实现:

/frameworks/native/libs/ui/FrambufferNativeWindow.cpp

hw_get_module(GRALLOC_HARDWARE_MMODULE_ID,&module) //加载模块

framebuffer_open(module,&fbDev)

gralloc_open(module,&grDev)//分别打开fb和gralloc

先得到buffer数量mNumBuffers,然后给buffer初始化:buffersi]=new NativeBuffer(width,height,format,GRALLOC_USAGE_HW_FB)

然后给buffer分配空间,grDev->alloc(grDev,fbDev->witdh,height,format,GRALLOC_USAGE_HW_FB,&buffers[i]->handle,&buffers[i]->stride)

为本地窗口赋值属性:ANativeWIndow::flags/xdpi/SwapInterval

ANativeWIndow::dequeueBuffer = dequeueBuffer;

...

这个函数很简单不再赘述,分析下FramebufferNativeWindow如何分配buffer的,也就是dequue方法获得的缓冲区从何而来,

成员变量mNumBffers代表了FramebufferNativeWindow管理的buffer总数。它取决于2个方面,首先从fb设备取值,fbDev->numFramebuffers,否则就用默认值define,最小2,最大3.

有人会问为什么FramebufferNativeWindow对应一个真实屏幕,却有多个buffer。因为要多缓冲机制。

再看FrambufferNativeWindow的构造函数,另一个问题:多个缓冲区空间从哪里分配的?通过前面可知,是向HAL层的Gralloc申请的。

FrambufferNativeWindow构造函数的1st for循环先给各个buffer创建相应的实例new NativeBuffer,然后调用Gralloc的alloc方法:

grDev->alloc(grDev,fbDev->width.height,format,GRALLOC_USAGE_HW_FB,。。。)

HW_FB代表缓冲区用途,定义在:/hardware/libhardware/incllude/hardware/Gralloc.h,目前支持实际中,

GRALLOC_USAGE_HW_TEXTURE,RENDER,2D,COMPOSER,FB,VIDEO_ENCODER(硬件视频编码器),

这里申请的缓冲区是要在屏幕显示的,所以是HW_FB,对应实现是[email protected],其他用途则是alloc_buffer,。不过,

如果底层只允许一个buffer,不支持page-flippingDe qingk ,name gralloc_alloc_framebuffer也同样可能只返回一个ashmem中申请的内存空间,而真正的真缓冲区则要在ipost时才用到。?????

所有申请到的缓冲区又FrambufferNativeINdow中的全局变量buffers[MAX_NUM_FRAME_BUFFERS]记录,每个数据元素是一个NativeBuffer,

它的定义如下:

class NativeBuffer:public ANativeObjectBase<ANativeWindowbBuffer,NativeBuffer>

可见这个本地缓冲区继承了ANativeWindowBuffer的特性,后者定义在/system/core/include/system/Window.h中

9.7.2 VSync信号处理

经过上面的分析,明白了如何产生Vsync,和流转过程。

VSYNc最终会被EventThread::threadLoop()分发给各个监听者,如SurfaceFlinger进程中就是MessageQueue。

MessageQueue通过与EventThread建立一个Connection来监听事件,

对Vsync等事件感兴趣的对象(如MessageQueue),要先通过调用接口EventTHread::createEventConnection()来建立一个连接

(应用进程是间接由SurfaceFlinger完成的),实际就是生生成了一个EventThread::Connection对象,这个对象将对双方产生如下影响:

△当Connection::onFirstRef时,即连接第一次被引用时,它会主动调用EventThread::registerDFisplayEventConnection()

把自己添加到EventThread的mDisplayEvent Connections中,这是保证事件发生后EvnetTHread能找到符合要求的连接的第一步。

△,当MessageQueue得到Conecction后,会马上调用getDataChannel来获得一个BitTube,

从逻辑关系看,Connnection只是双方业务上的连接,而BitTube才是数据传输通道,各种event信息是通过这里传输的,

下面以messageQueue为例分析各个进程如何与MessgeThread交互:

MessageQueue::setEventTHread

mEventThread=envetThread->createEventConnnection()//建立一个connection

mEventTube = mEvents->getDtateChaneel(),//立即获取BitTube

mLooper->addFd()

EventTHread是serverr,不断往Tube写入数据,MessageQueue是Client,负责读取数据。

MessqageQueue如何得知有Event到来然后读取?答案是他们之间的数据读写模式采用的是socket(AF_UNIX域)

上函数结尾通过Looper添加了一个fd,这就是socket pair的一端,

然后Looper将这个fd与其callback函数(MeesaveQueue::cb_eventReceiver)加入全局的mRequests进行管理:

KeyedVector(int ,request) mRequests ;

这个Vector会集中所有需要监测的fd,这样当Looper进行pollInner时,只要有事件需要处理,

他就可以通过callback函数通知接收者,实现细节在BitTube.cpp和Looper.cpp,

当Event发生后,MessageQueueL::cb_eventReceiver开始执行,进而调用eventReceiver,

如果event类型是DisplayEventReceiver::DISPALY_EVENT_VYSNC,则正是我们想要监听的时间。

这时有两种情况:

if(buffer[i].heaer.type == DisplayEventReciver :: DISPLAY_EVENT_VYSNC)

if INVALIDATE_ON_VYSNC mHandler->displayInvalidate()

else mHandler->dispatchRefresh()

宏INVALIDATE_ON_VYNC默认是1,什么意思?

我们知道在把UI刷新到屏幕上refresh之前,各个UI进程需要先准备好数据(invalidate),

1,那SurfaceFLinger是在vsync来临时再做数据的准备工作,然后立刻刷新到屏幕,

2,还是平常就准备,当vysnc来临时吧数据刷新到屏幕?

当INVALLIDATE_ON_VYSN是1,程序执行操作1,

否则2.

dispatchInvalidate:

handleMessage::TranSaction(),

handleMessageInvalidate();

signalRefresh()

displachRefresh:

case MessageQueue::REFESH:

handleMeesage Refresh

signalRefresh最终的处理和handleMessageRefresh一样,可见INVALIDATE的情况下多执行了两个函数handleMessageTreansaction和hanldeMessageInvlaidate,他们分别用于处理

业务(client发起的对Layer属性的变更业务,后续讲解)和

数据Invalidate

先看看handleMessageRefresh的工作,也是后续小节的重点:

void SurefaceFlinger::handleMessageRefresh(){

preComposition(); //合成前的准备

rebuildLayerStacks()//重新建立Layer堆栈

setUpHWComposer() //HWCOMposer的设定

deComposition() //正式的合成工作

postCompostion //合成的后期工作

}

jellyBean的4.1-4.3对surfaceFlinger渲染UI的处理过程改动很大,但4.3到达了很好的状态,代码风格和逻辑关系比前两个提高很多,

上面这些函数加上handleMessageTreansacation/Invalidata就基本涵盖了SurfaceFlinger的所有功能,

下面分析他们:

9.7.3 handleMessageTransaction

做了简单判断年后,直接调用handleTransacation,然后handleTreansactrion获取mStateLock锁,然后才可以进一步调用handleTransactionLocaked,

最后将mHwWorkListDiry(后面看到它的作用)置为true,

显然后一个handleTransactionLocked才是真正处理业务的地方,具体工作分2不,

traversal(对应eTraversalNeeded)和

transaction(对应eTransactionNedded)

void surfaceFlinger::handleTransactionLocked(transactionFlags)

{

currentLayers(CurrentSate.layerSortedByZ)

conunt = currentLayers.size)();

if(transactionFlags & eTraversalNeeded) // 1,是否需要traversal

{

for( i< count) //逐个layer处理

{

const LayerVesctor& currentLayers(mCurrenState.layersSortedByZ)

rtFlags = layer->getReansacionFlgas(eTransactionNeeded) // part 1 ,这个layer是否需要doTransaction

if(!rtFlags) continue;

flags = layer->do Tansaction(0); //各layer做内部处理,后面做详细分析

if (flags & layer::eVisibleRegion) //各个layer计算可见区域是否发生变法

mVisibleRegionsDisry == true //可见区域变化

if(trasactionFlags & eDisyalYTransactionNeeded) ....

if(transactionFlags & (eTraversalNedded | eDispalyTransactionNedded)) ...

..//part 2 , SurfaceFlinger内部产生的Transaction 处理

commitTransaction() //提交结果

}

}

}

TransactionFlags分为3种类别:

enum{

eTransactionNeeded = 0x01

eTraversalNeeded = 0x02

eDisplayTransactionNeeded = 0x04

eTransactionMask = 0x07 //用于取值

}

part1是围绕这3个flag展开的,因为各个flag的处理过程基本相似,置讲解eTraversalNeeded讲解,

part1,处理各个flags,eTarersalNeeded,需要做一个Traversal操作,也就是遍历所有Layers,

SurefaceFlinger记录当前系统layers状态的有两个全局变量,

state mDrawingState;l

state mCurrenState;

前者是上一次drawing时的状态,后者是当前状态。

这样我们只需要通过对比这两个state,就知道layer发生的变化,而采取相应措施。

他们内部都包含了一个layerVector类型的layersSortedBYZ成员变量,

从变量名字可以看出所有的layers按照Z-ordere顺序排列而成的Vector,

所以我们可以通过mCurrentState.layersSortedByZ来遍历所有的layers,然后对其中需要执行transaction的layer调用内部的doTransaction()。

显然不是每个Layer在每次handleTransactionLocked中都要调用doTransaction,

判断标准就是Layer::getTransactionFlags返回的标志中是否指明了eTransactionNeeded,

SurfaceFLInger和layer都有一个mTransactionFLags变量,但含义不同,

另外,layer对象也同样有mCurrentSate和mDrawingState,却属于完全不同的Struct数据结构,

为了理解各个layer究竟在doTransaction做了什么,先穿插分析它的实现,

uint32_t Layer::doTransaction(uint32_t flags)

{

const Layer::State& front(drwaingSate()); //mDrawingState

const Lyaer::State& temp(currentSate()) //mCurrentState

const bool sizeChangere = (temp.requested.w != front.requested.w) || (temp.requested.h != front.requested.h);

if(sizeChanged){ // 尺寸变化

mSurfaceFLingerConsumer->setDefaultBufferSize(tem.requested.w , temp.requested.h)

}

if(front.active!= temp.active){ //重新计算可见区域

flags |= Layer::eVisibleRegion;

}

if(temp.sequence != front.sequence) {//什么是sequence?

flags |= eVisibleRegion; //也要重新计算可见区域

this->contentDirty = true ;

const uint8_t type = temp.transform.getType();

mNeedsFiltering = (!temp.transform.preserveRects()) || (type >= Transform::SCALE)

}

commitTransaction()

returen flags

}

首先判断layer的尺寸是否发生变量,sizeChanged变量,即当前状态temp中的宽高与上一次窗台front的是否一致。

加入size变量,则调用mSurfaceFLingerrConsumer->setDefaultBufferSize()来是它生效,

其中mSurfaceFlingerConsumer在前几个小节分析过,

函数setDefaultBufferSize()改变的是内部的mDefaluWith/Height两个默认的的宽高值,

当调用requestBuffers()请求buffer是,如果没有指定width和hright,BufferQueue就会使用这个默认值,

接下来的难点就是理解sequence,

这个变量是一个int值,当layer的positon,z-order,alpha,matrix,transparent透明 region ,flags , crop裁剪,等一系列属性变化时

(这些属性的设置函数以setXXX开头,setCrop, setFlags,)

,mCurrentSate::sequence就会自增。

因而当doTransaction时,它和mDrawingState::sequence的值就不同了,

相比于每次属性变化都马上处理,这样设计是合理的,

因为它在平时只收集属性的变化,直到特定的时刻(VSYNC信号产生时)才会做统一处理,

一方面,节约系统资源,

另一方面,如果属性频繁变化,会给整个画面带来不稳定感,

仔细观察layer的doTransaction实现,可以大致得出它的目的

通过分析当前与上一次状态的变化,指定下一步的操作,比如是否要重新计算可见区域eVsibleRegion,

是否需要重绘contentDirty等,

这些操作有的是由SurfaceFLinger统一部署的,因而通过函数的返回值flags传递给SurfaceFlinger,如eVisibleRegion;

有些则属于Layer的家务,因而只要内部做好标记就可以,

最后,commitTransaction()把mCurrentSate赋值给mDrawingState,这样他们两个的状态有意义了,

下一轮doTransaction前,mCurrenState又会随着属性的变更(如UI 程序clinet主动发起的申请)而产生变化,往复循环,

这样Layer::doTransaction()函数就结束了,带着flags只返回到前面的SurfaceFLinger::handleTransactionLocked()宏,

一旦某个Layer明确的表明了可见区域发生了改变eVisbleRegion,

SurfaceFlinger就会将其mVisibleRegionsDirty设置为true,这个标志会影响后续操作,

ppart2,内务处理,

完成系统所有layer的遍历,SurfaceFLinger进入自己的内务处理,

即handleTransactionLocked()源码的中第二部分,代码中列出了它需要完成的工作:

△ 新增layer

与上一次处理时相比,系统可能有新增的layer

只要对比两个state中的layer队列书里是否一致就可以,

如果书里增加,可见于去要或从新近似UAN,则将mVisibleRegionsDirty置为true

△移除Layer

和上面的情况类似,有些layer也可能被移除了,

这时也要重新计算可见区域,

可是怎么知道哪些layer被移除?简单办法就是比较两个state中的layersSortedByZ

加入一个layer在上一次的mDrawingSate中海油,这次mCurrenSate没有了,那么就是被removed了,

我们需要计算这些被移除layer的可见于去,因为一旦layer被移除,则被他覆盖的区域就可能重新显露出来,

在handleTransacionLocked()末尾,它也调用了commitTransaction()来结束整个业务处理,

另外,SurfaceFlinger还需要通知所有被移除的layer,响应的callback函数是onRemoved(),

layer在得到消息后,知道被surfaceFLinger剔除了,唤醒所有正在等待Transaction结束的线程:

mTransactionCV.broadcast();

SurfaceFLinger::handleTransaction()流程:

orientation,方向变化,

9.7.4 界面已经过时,无效,需要重新绘制,handleMessageInvalidate

Invalidate的字面意思是使无效,

在不少窗口系统用来表示界面已经过时,无效,要重新绘制,

SurfaceFlinger中,与Invalidate关系最直接的是buffer数据,

因而handleMessageInvalidate实际上只是简单的调用了另一个函数handlePageFlip。

PageFlip可以理解为翻页,从字面意思理解,它的确与图层缓冲区有关系,

因为是多缓冲机制,那么再适当的实际就要做翻页动作,核心源码:

void SurfaceFlinger::handlePageFlip()

{

Region dirtyRegion;

bool visibleRegions = false ;

const LayerVector& currentLayers(mDrawingSate.layersSortedByz)

const size_t conunt = currenLayers.size()'

for(i<conunt)

{

const sp<layer>& layer(currentLayers[i])

const Region dirty(layer->latchBuffer(visibleRegions)) // step1 , 锁住buffer

const Layer::State& s(layer->drawingState());

invalidateLayerStack(s.laerStack,dirty) //step2,更新dsiplayDevice中的脏区域

}

mVisibleRegionDirty|= visibleRegions;

}

step1 @ SurfaceFLinger::handlePageFlip

通过latchBUffer分别锁定各个layer当前要处理的缓冲区,

源码作者用了又去的次latch,形象的表达了把门闩上,锁住buffer的意图,

可想而知吗,这个函数一定和bufferqueue有直接联系:

region layer::latchBuffer(bool& recomputeVisibleRegions)

{

Region outDirtyRegion;

if(mQueuedFrames > 0) //有需要处理的frame

{

const bool oldOpacity = isOpaque(*); // 不透明

sp<GraphicBUffer> oldActiveBufcer = mActiveBuffer;

if(andrdoi_atomic_dec(&mQueuedFrames)>1)

mFlinger->signalLayerUpdate();

//定义一个名为Reject的struct,代码略,

Reject r(mDrawingState , currentrState() , recomputeVisibleRegoins)

if*(mSurfaceFlingerConsumer->updateTexImage(&r) != NO_ERROR)

//异常处理,并结束函数

mActiveBUffer = mSurfaceFlingerConsumer->getCurrentBuffer();

..

glTexParameterx(GL_TEXTURE_EXTERNAL_OES,GL_TEXTURE_WRAP_S , GL_CLAM_TO_EDGE)

const Layer::State& front(drawingState())

Region dirtyRegion(Rect(front.active.w , front.active.h))

outDirtyRegion -= (front.transform.transform*(dirtyRegion))

}

return outDistyRegion

}

Layer中继承了FrameAvaliableListener,专门用于监听onFrameAvailable,

因而当BUfferQueue中的一个BufferSlot被queued后,会通知这个Listener,进而调用所属layer的onFrameQueued

--这个函数会增加mQueuedFrames的计数,并且向suerfaceFlinger发出一个INVALIDATE信号(signalLayerUpdate)

如果当前没有任何queued buffer, latchBuffer什么都不用做,

如果当前有多个mQueuedFrames,除了正常处理wait,还要另外调用signalLayerUpdate来组织一次新的invalidate

layer中持有一个SurfaceFLingerConsumer对象,成员变量mSurfaceFlingerConsumer,用于管理BufferQueue,

根据前面对BufferQueue状态迁移的分析,

一旦surfaceFLInger(消费者)需要对某个buffer操作,首先应该acquire它,

这个动作被封装在SurfaceFlingerConsumer::updateTexImage中,

除此之外,这个函数还需要根据buffer中的内容来更新Texture,稍后做详细分析,

在latchBuffer内部定义了一个Reject结构体,

并且作为函数参数传递给updateTexIamge,

后者在acuire到buffer后,主动调用Reject::reject来判断这个缓冲区是否符合SurfaceFlinger的要求

如果updateTexIamge成功,Layer就可以更新mActiveBUffer了,

即前面活跃的缓冲区(就没个layer对应32个bufferslot)

以及其他一系列相关内部成员变量,如mCurrenCrop,mCurrentTransform等,代码略,

纹理贴图还涉及很多细节的配置,比如纹理图像与目标的尺寸大小可能不一致,这种情况如何处理?

或者当纹理坐标超过[0.0, 1.0]范围,怎么解决?

这些具体的处理方式可以通过调用glTexParameterx(GLenum targetg, GLenum pname ,GLfixed param)配置,

函数的第二个参数是需要配置的对象类型(比如GL_TEXTURE_WRAP_S , GL_TEXTURE_WRAP_T分别代表两个坐标维),m

第三个参数param就是具体的配置,

status_t SurfaceFingerConsumer::updateTerxIamge(BufferRejecter* rejectrer)

{

...

Mutex::Autolock lock(mMutex)'

...

bufferQueue::BufferItem item;

err = acquireBufferLocked(&item); //acuiqre一个buffer

if(rejecter && rejecter->reject(mSlots[buf].mGraphicBUffer, item)) //这个buffer是否符合要求?

{

releaseBufferLocked(buf, EGL_NO_SYNC_KHR)

return NO_ERRIR , //注意,reject返回false是接受,否则才是拒绝

}

//生成 Texture

err = releaseAndUpdateLocked(item)

...

return err;

}

这个函数比较好仓,只保留了最核心的部分

它的目标是更新layer的纹理texture,分为如下几个步骤:

1)acquire一个需要处理的buffer,

根据前面对buffer状态迁移的分析,当消费者想处理一个buffer是,要先向bufferQueue做acquire申请,

那么bufferqueue怎饿知道当前要处理的是哪一个buffer?

这是因为它内部维护一个fifo先入先出队列,

一旦有buffer被enqueued,就会压入队尾,

每次acquire就从队头取出最前面的元素进行处理,完成之后再从队列移除,

2)Acquire'到的buffer封装在bufferItem中,

item.mBuf代表它在bufferSlot中的序号,

假如mEGLSlots[buf].mEglImage当前不为空(EGL_NO_IMAGE_KHR),

则需要先把就的image销毁,eglDestroyInmageKHR

3)SurfaceFLinger有权决定updateTexIamge得到的buffer是否为合法有效的,比如说size是否正确,

这是通过updateTexIamge(BUfferRejecter* rejecter)中的reject对象完成的,

bufferRejecter实现了一个reject接口,用于验证前面acquire到的buffer是否符合SurfaceFlinger的要求,

4),接下来就可以生成Texture了,

内部实现要分别调用glBindTexure,glEGLImageTargetTexture2DOES,

后一个函数是OGLES中对glTexIamge2D的口占,因为嵌入式环境如果直接用glTerxIanmge2D,一旦图片很大会

严重影响执行速度,扩展可以避免这个问题,

这个接口是和eglCreateIamgeKhR配套使用的,被封装在前面的createIamge中,

不了解这些函数的请自行查询GLES技术文档,

5)消费者完成buffer的处理,就可以release了,这个buffer又恢复到FREE状态,

以供生产者再次dequeue使用后,需要更新GLConsumer(SurfaceFlingerConsumer的父类)中的各个成员变量,

包括mCurrentTexture , m CurrentTextureBuf等,

流程:

step2@SurfaceFlinger::handlePageFlip,

通过invalidateLayerStack来更新各个DisplayDevice中的dirtyRegion。

内部实现简单,自行给分析。

9.7.5,合成前的准备工作,preComposition

字面理解就是预合成,

即合成前的准备工作,

分析preComposition的具体工作前,有必要先了解下VSync Rate,

IDisplayEventConnection提供了与Vysnc rate有关的两个接口:

virtual void setVysncReate(uint32_t count ) = 0;

virtual void requestNextVsync() = 0 //asynchronous

他们之间具有互斥的关系:

当setVsyncRate = 1,表示每个Vsync信号都要报告,

数值2表示隔一个Vysnc报告一次,

0表示不报告,除非有人主动调用了requestNextVsync。

同理,除非setVysncReate设置的值为0,否则调用requestNextVysnc安排下一次Vysnc是无效的,

接下来看preComposition:

void SurfaceFlinger::preCompostion()

{

bool needExtraInvalidate = false ;

const LayerVector&currentLayers(mDrawingState.layersSortedByZ) //当前所有layer的集合,

const size_t count = currentLayers.size() //layer数量,

for(i<count)

{

if(currentLayers[i] -> onPreCompostion()) //调用每个layer的预合成接口,

needExtraInvalidate = true

}

if(neddExtralinvalidate)

signalLayerUpdate() //这个函数是做什么的?

}

这个函数逻辑简单,编译所有layers,调用每个layers的onPreCompostion,

就是每个人都做准备工作,需要什么准备?

bool Lyaer::onPreCompostion()

{。。。

return mQueuedFrames >0;

}

可见当onPreCompostion的返回值是true时,表示mQueuedFrames>0,否则就是false

结合SurfaceFlinger::preCompostion的判断,只要有一个Layer中存在被Queued的Frames,

那么needExtraInvalidate都会变成true,这是就会触发如下的函数调用流程:

SurfaceFlinger::signalLayerUpdate->MessageQueue::invalidate->

IDisplayEventConnection::requestNextVsync->EventThread::requestNextVsync:

void EventThead::requestNextVsync(const sp<EventThead::Connection> & connection)

{

Mutex::Antulocl _l(mLock)

if(connection->count <0) // <0就是disabled

{

connection_.count = 0; // = 0 说明还有可以接受一次eevent;

mCondition.broadcast() //通知EventThread,有人对Vsync感兴趣

}

}

这里的count是Connection类的成员变量,含义如下:

>= 1时,可以连续性的接收事件,

=0时,一次性事件,

=-1,事件接收被disabled

所以在requestNexyVysn中,若count小于0,则=0,代表这个connection可以接收一次事件,

最后,requestNextVysnc会调用mCondition.broadcast(),谁在等待?

还是EventThread,我们知道EventThread::threadLoop会不断的调用waitForEvent来等待Event,

这个函数内部还有一个重要的判断,就是当前是否有对Vsync信号感兴趣的人,

(基于所有Connection中的count状态判断),有的话正常返回,否则调用:

mCondition.wait(mLock)进入等待,直到有人唤醒它,

9.7.6,可见区域,rebuildLayerStacks

经过preCompostion,SurfaceFlinger::handleMessageRefresh接着会调用rebuildLayerStacks,

这个函数负责rebuild所有可见的layer(visible layer),

核心思想就是计算有哪些layer是可见的,和他们的可见区域,

void SurefaceFlinger::rebuyildLayerStacks()

{

if(CC)UNLIKERY(mVisibleRegionSDirty))

{...

mVisibleRegionsDirty = false ; //重置变量

invalidateHwcGeometry(); //将mHwWorkListDiry置为true,后续用到,

const LayerVector& currentLayes(mDrawingState.layesSortedByZ) //当前所有的layers

for(dpy<mDispolays.size()) //step1 , 分别计算每种dispaly的情况

{

region opaqueRegion //不透明区域

region dirtyRegion .//.脏区域

Vector<sp <layer>> layersSortedByZ ; //最终排序后的可见layers

const sp<DisplayDevice>& hw(mDisplays[dpy]) //每个display都有一个DisplayDevice

constructionTransFor& tr(hw->getTransform())//Transform变换

const Rect bounds(hw->getBounds()) //Display的区域

if(hw->canDraw()) // 当前这个display是否可以绘制

{

SurfaceFlinger::computeVisibleRegions(currentLayers,

hw->getLayerStack(),dirtyRegion , opaqueRegion) //step2,计算可见区域

for(currentLayers.size()) //step3,逐个计算各个layer

{

sp<Layer>& layer(currentLayers[i]);

Layer::State& s(layer->drawingState())

if(s.layerStack == hw-0>getLayerStack()) // 这个layer是否属于该display

{

region drawRegion(tr.ransform(layer->visibleNonTransparentRegion))

drawRegion.andSelf(bounds)

if(! drawRegion.isEmpty()) //如果绘制区域不为空,说明是需要被处理的,

layerSortedByZ.add(layer)

}

}

}

//step4 , 将前述的计算结果保存到hw中

hw->setVisibleLayerSortedByZ(layersSortedByZ)

hw->undefinedRegion.set(bounds)

hw->uindefinedRegion.subtractSelf(tr.transform(opaqueRegion))

hw->dirtyRegion.orSelf(dirtyRegion)

}

}

}

step1@SurfaceFlinger::rebuildLayerStacks

前面说过,系统的display可能不止一个,他们都存储在mDisplays中,

rebuldLayerStacks需要逐个display处理,

这个函数涉及的与display有关的全局属性有:

△,Transform mGlobalTransform

整个display都需要做的transform,各个layer也可以有自己的transfoirm

△int mDisplayWidth/Hright

Display的宽和高,上述代码中的bounds变量(hw->getBounds)得到的是:

rect(mDisplayWidth,Height)

step2@SurfaceFlinger::rebuildLayerStacks

调用computeVisibleRegions,这个函数将根据所有Layer,也就是currentLayers的当前状态,

计算出两个重要变量

△dirtyRegion

脏区域,也就是要重新绘制的区域

opaqueRegion

不透明区域,它会对Z-ordered的layers产生影响,

因为排在前面的layer的不透明区域可能会挡住后面的layer

computeVisbleRegions的源码很长,后面单独分析,

sttep3

计算出VisibleRegions后,要进一步确定各个layer要被重绘的区域drawRegion

首先要明白系统中可用的display不只有一个,

但是偶有的layers却都是通过mDrawingState,LayersSoredByZ来记录的,

显然系统需要一个机制来区别各个layer的所属方,这就是LayerStack的作用,

这个disoplay有一个独特的标志,作为SUrfaceFlinger管理他们的依据。

所以在第二个for循环,先判断了s.layerStackshifou dengyu hw->getLayerStack,

是才下一步处理,变量drawRegion的计算过程分两步:

1,对layer中的visibleNonTransparentRegion按照整个display的Transform要求进行变换,

其中visibleNonTransparentRegion如其名所示,代码了layer的可见不透明区域,是由前面的computeVisibleRegions计算出来的,

2,在上述区域drawRegion的基础上考虑bounds,即整个Display的区域限制,

最后得到的drawRegion如果不为empth,说明这个layer在此次UI刷新过程中是需要被重新绘制的,因而把它加入layersSortedByZ中,

我们处理layer的过程是按照Z-Order顺序做的,所以他们加入layersSoredByZ时,也同样已经SoredByZ-Order了,

step4,

将计算结果保存layersSortedByz,opaqueRegion等保存在hw中,这些数据对后面合成起到了关键的作用,

没有看完,

9.7.7,为Composition搭建环境,setUpHWComopser

经过前面努力,合成操作所需的各种数据已经准备好,

接下来用setUpHWCoimposer为Composition搭建环境,

关于HWComposer我们在前几个小节已经接触过,要特别注意源码工厂有两个HWComposer.h,

hardware/libhardware/include/hardware/HWComposer.h

frameworsks/native/sservice/surfacelinger/displayhardware/HWComposer.h

1st HWComposer是属于HAL模块的定义,

2nd则是SurfaceFlinger管理HWCmoposer模块二涉及的,

SurfaceFLinger::readyToRun中,程序会调用:

mHwc = new HWCmopooser(this , *static_cast<HWComposer::EventHandle *>(this))

此时生成的HWCoimposer担当的是管理者的身份,它的构造函数中有进一步通过

loadHwcModule来加载名称为HWC_HARDWARE_)MODULKE)_ID的HWComposer模块,

先了解下HAL层的HWComposer的接口:

interface

prepare

每帧图像合成前,都会被调用,用于判断HWC模块再合成操作中能够完成的功能,

3rd参数hwc_dis0play_contents_1_t** displays有个hwc_layer_1_t hwLayer[0]成员变量,

而hwc_layer_1_t中的compositionType则是HWC对prepare的回应,

详细的ConpositonType类型见后面表格,

set

set用于代替早起版本的eglSwapbuffers

,因而功能基本相同,通过前一个prepare,现在SurfaceFlinger和HWC模块已经达成了共识,

即哪些Layer是可以由HWC来处理的,也就是源码所称的WorkList

所以set函数就讲这些layer的具体数据传送给HWC做实际的操作,

特别注意,set中传入的layer list必须与prepare计算出来的结果一致,

可以才想到,set内部实现 一定会调用eglSwapbuffers,

eventControl

控制diplay相关的events个开关,如HWC_EVNT_VYSNC

blank

屏幕的开关

query

查询HWC的状态

registerProcs

向hwc注册callback函数

CompostionType

HWC_BACKGROUND

在prepare前被调用者设置的值,表明这是一个特别的background图层,

此时只有backgroundColor是有效的,

HWC_FRAMEBUFFER_TARGET

也是在prepare前被调用者设置的值,

表明此图层是OGLES合成时的目标framebuffer suraface,

只在HWC_DEVICCE_API_)VERSION_1_1以后有效

HWC_FRAMEBUFFER

在prepare前被调用者设置的值,切要求HWC_GEOMERY_CHANGED也应该置位,

表明Layer将通过OGLES绘制到framebuffer中

HWC_OVERLAY

prepare过程中由HWC模块设置的值,

表示该图层由HWC处理,而不是像上面的HWC_FRAMEBUFFER那样由GLES绘制

上述表明,合成过程可以由HWC完成,也可以通过GLES处理,

哪种方式是由prepare的结果,即compostionType决定的,

使用者可向HWC模块注册callback函数接收相关事件:

typedef strct hwc_procs

{

void (*invalidate)(const struct hwc_procs* procs) //触发屏幕刷新

void (*vsync)(const struct hwc_procs* procs , int disp , int64_t tiemsptamp)

//首先通过前面表格中的eventControl接口将HWC_ENVET_VSYNC事件打开,

//这样后续如果有Vsync信号就回调次函数,

void (*hotplug)(const struct hwc_procs* procs , int disp , int connected)

//当一个display被connected或者disconnected时回调次函数,

//初一Primary类型的display必须一致是connected的,所以不使用这个函数

}

了解了以上只是,可以分析setUpHWComposer的源码了

void SurfaceFlinger::setUpHWComposer

{

}

猜你喜欢

转载自blog.csdn.net/u012654756/article/details/89708085