深入理解android内核设计思想 第九章 笔记
第九章,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
可见这个本地缓冲区继承了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 { const sp 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 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¤tLayers(mDrawingState.layersSortedByZ) //当前所有layer的集合, const size_t count = currentLayers.size() //layer数量, for(i { 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 { 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 { region opaqueRegion //不透明区域 region dirtyRegion .//.脏区域 Vector const sp 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::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 此时生成的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 { }