8.5.2 SF工作线程分析
SF中的工作线程就是用来做图像混合的,比起AudioFlinger来,它相当简单,下面是它的代码:
[—>SurfaceFlinger.cpp]
bool SurfaceFlinger:threadLoop()
{
waitForEvent();//①等待什么事件呢?
if(UNLIKELY(mConsoleSignals)){
handleConsoleEvents();
}
if(LIKELY(mTransactionCount==0)){
const uint32_t mask=eTransactionNeeded|eTraversalNeeded;
uint32_t transactionFlags=getTransactionFlags(mask);
if(LIKELY(transactionFlags)){
//Transaction(事务)处理,放到本节最后来讨论。
handleTransaction(transactionFlags);
}
}
//②处理PageFlipping工作。
handlePageFlip();
const DisplayHardware&hw(graphicPlane(0).displayHardware());
if(LIKELY(hw.canDraw()&&!isFrozen())){
//③处理重绘。
handleRepaint();
hw.compositionComplete();
//④投递BackBuffer。
unlockClients();
postFramebuffer();
}else{
unlockClients();
usleep(16667);
}
return true;
}
ThreadLoop一共有四个关键点(即①~④),这里分析除Transaction外的三个关键点。
1.waitForEvent
SF工作线程一上来就等待事件,那会是什么事件呢?来看代码:
[—>SurfaceFlinger.cpp]
void SurfaceFlinger:waitForEvent()
{
while(true){
nsecs_t timeout=-1;
const nsecs_t freezeDisplayTimeout=ms2ns(5000);
……
MessageList:value_type msg=mEventQueue.waitMessage(timeout);
……//还有一些和冻屏相关的内容。
if(msg!=0){
switch(msg->what){
//千辛万苦就等这一个重绘消息。
case MessageQueue:INVALIDATE:
return;
}
}
}
}
SF收到重绘消息后,将退出等待。那么,是谁发送的这个重绘消息呢?还记得在unlockCanvasAndPost函数中调用的signal吗?它在SF端的实现代码如下:
[—>SurfaceFlinger]
void SurfaceFlinger:signal()const{
const_cast<SurfaceFlinger*>(this)->signalEvent();
}
void SurfaceFlinger:signalEvent(){
mEventQueue.invalidate();//往消息队列中加入INVALIDATE消息。
}
2.handlePageFlip分析
SF工作线程从waitForEvent中返回后,下一步要做的就是处理事务和handlePageFlip了。先看handlePageFlip,从名字上可知,它和PageFlipping工作有关。
注意 事务处理将在8.5.3节中介绍。
代码如下所示:
[—>SurfaceFlinger.cpp]
void SurfaceFlinger:handlePageFlip()
{
bool visibleRegions=mVisibleRegionsDirty;
/*
还记得前面所说的mCurrentState吗?它保存了所有显示层的信息,而绘制的时候使用的mDrawingState则保存了当前需要显示的显示层信息。
*/
LayerVector¤tLayers=
const_cast<LayerVector&>(mDrawingState.layersSortedByZ);
//①调用lockPageFlip。
visibleRegions|=lockPageFlip(currentLayers);
const DisplayHardware&hw=graphicPlane(0).displayHardware();
//取得屏幕的区域。
const Region screenRegion(hw.bounds());
if(visibleRegions){
Region opaqueRegion;
computeVisibleRegions(currentLayers,mDirtyRegion,opaqueRegion);
mWormholeRegion=screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty=false;
}
//②调用unlockPageFlip。
unlockPageFlip(currentLayers);
mDirtyRegion.andSelf(screenRegion);
}
hanldePageFlip调用了两个看起来是一对的函数:lockPageFlip和unlockPageFlip。这两个函数会干些什么呢?
(1)lockPageFlip分析
先看lockPageFlip函数,代码如下所示:
[—>SurfaceFlinger.cpp]
bool SurfaceFlinger:lockPageFlip(const LayerVector¤tLayers)
{
bool recomputeVisibleRegions=false;
size_t count=currentLayers.size();
sp<LayerBase>const*layers=currentLayers.array();
for(size_t i=0;i<count;i++){
const sp<LayerBase>&layer=layers[i];
//调用每个显示层的lockPageFlip。
layer->lockPageFlip(recomputeVisibleRegions);
}
return recomputeVisibleRegions;
}
假设当前的显示层是Layer类型,那么得转到Layer类去看它的lockPageFlip函数了,代码如下所示:
[—>Layer.cpp]
void Layer:lockPageFlip(bool&recomputeVisibleRegions)
{
//lcblk是SharedBufferServer类型,调用retireAndLock函数将返回FrontBuffer的
//索引号。
ssize_t buf=lcblk->retireAndLock();
……
mFrontBufferIndex=buf;
//得到FrontBuffer对应的GraphicBuffer。
sp<GraphicBuffer>newFrontBuffer(getBuffer(buf));
if(newFrontBuffer!=NULL){
//取出脏区域。
const Region dirty(lcblk->getDirtyRegion(buf));
//和GraphicBuffer所表示的区域进行裁剪,得到一个脏区域。
mPostedDirtyRegion=dirty.intersect(newFrontBuffer->getBounds());
const Layer:State&front(drawingState());
if(newFrontBuffer->getWidth()==front.requested_w&&
newFrontBuffer->getHeight()==front.requested_h)
{
if((front.w!=front.requested_w)||
(front.h!=front.requested_h))
{
……//需要重新计算可见区域。
recomputeVisibleRegions=true;
}
mFreezeLock.clear();
}
}else{
mPostedDirtyRegion.clear();
}
if(lcblk->getQueuedCount()){
mFlinger->signalEvent();
}
/*
如果脏区域不为空,则需要绘制成纹理,reloadTexture将绘制一张纹理保存在mTextures数组中,里边涉及很多OpenGL的操作,读者有兴趣可以自己研究。*/
if(!mPostedDirtyRegion.isEmpty()){
reloadTexture(mPostedDirtyRegion);
}
}
我们知道,Layer的lockPageFlip将根据FrontBuffer的内容生成一张纹理。那么,unlockPageFlip会做些什么呢?
(2)unlockPageFlip分析
unlockPageFlip的代码如下所示:
[—>SurfaceFlinger.cpp]
void SurfaceFlinger:unlockPageFlip(const LayerVector¤tLayers)
{
const GraphicPlane&plane(graphicPlane(0));
const Transform&planeTransform(plane.transform());
size_t count=currentLayers.size();
sp<LayerBase>const*layers=currentLayers.array();
for(size_t i=0;i<count;i++){
const sp<LayerBase>&layer=layers[i];
//调用每个显示层的unlockPageFlip,Layer的unlockPageFlip主要做一些
//区域的清理工作,读者可以自己看看。
layer->unlockPageFlip(planeTransform,mDirtyRegion);
}
}
(3)关于handlePageFlip的总结
handlePageFlip的工作其实很简单,以Layer类型为例来总结一下:
各个Layer需要从FrontBuffer中取得新数据,并生成一张OpenGL中的纹理。纹理可以看做是一个图片,这个图片的内容就是FrontBuffer中的图像。
现在每一个Layer都准备好了新数据,下一步的工作当然就是绘制了。来看handleRepaint函数。
3.handleRepaint函数分析
handleRepaint函数的代码如下所示:
[—>SurfaceFlinger.cpp]
void SurfaceFlinger:handleRepaint()
{
mInvalidRegion.orSelf(mDirtyRegion);
if(mInvalidRegion.isEmpty()){
return;
}
……
const DisplayHardware&hw(graphicPlane(0).displayHardware());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
uint32_t flags=hw.getFlags();
i f((flags&DisplayHardware:SWAP_RECTANGLE)||
(flags&DisplayHardware:BUFFER_PRESERVED))
{
……//计算mDirtyRegion。
}
//在脏区域上进行绘制。
composeSurfaces(mDirtyRegion);
mDirtyRegion.clear();
}
其中,composeSurfaces将不同的显示层内容进行混合,其实就是按Z轴的顺序由里到外依次绘制。当然,最后绘制的数据有可能遮盖前面绘制的数据,代码如下所示:
[—>SurfaceFlinger.cpp]
void SurfaceFlinger:composeSurfaces(const Region&dirty)
{
const SurfaceFlinger&flinger(*this);
const LayerVector&drawingLayers(mDrawingState.layersSortedByZ);
const size_t count=drawingLayers.size();
sp<LayerBase>const*const layers=drawingLayers.array();
for(size_t i=0;i<count;++i){
const sp<LayerBase>&layer=layers[i];
const Region&visibleRegion(layer->visibleRegionScreen);
if(!visibleRegion.isEmpty()){
const Region clip(dirty.intersect(visibleRegion));
if(!clip.isEmpty()){
layer->draw(clip);//调用各个显示层的layer函数。
}
}
}
}
draw函数在LayerBase类中实现,代码如下所示:
[—>LayerBase.cpp]
void LayerBase:draw(const Region&inClip)const
{
……
glEnable(GL_SCISSOR_TEST);
onDraw(clip);//调用子类的onDraw函数。
}
至于Layer是怎么实现这个onDraw函数的,代码如下所示:
[—>Layer.cpp]
void Layer:onDraw(const Region&clip)const
{
int index=mFrontBufferIndex;
if(mTextures[index].image==EGL_NO_IMAGE_KHR)
index=0;
GLuint textureName=mTextures[index].name;
……
Region holes(clip.subtract(under));
if(!holes.isEmpty()){
clearWithOpenGL(holes);
}
return;
}
//index是FrontBuffer对应生成的纹理,在lockPageFlip函数中就已经生成了。
drawWithOpenGL(clip,mTextures[index]);//将纹理画上去,里面有很多和OpenGL相关的内容。
}
drawWithOpenGL函数由LayerBase实现,看它是不是使用了这张纹理,代码如下所示:
[—>LayerBase.cpp]
void LayerBase:drawWithOpenGL(const Region&clip,const Texture&texture)const
{
const DisplayHardware&hw(graphicPlane(0).displayHardware());
const uint32_t fbHeight=hw.getHeight();
const State&s(drawingState());
//validateTexture函数内部将绑定指定的纹理。
validateTexture(texture.name);
//下面就是OpenGL操作函数了。
glEnable(GL_TEXTURE_2D);
……
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
//坐标旋转。
switch(texture.transform){
case HAL_TRANSFORM_ROT_90:
glTranslatef(0,1,0);
glRotatef(-90,0,0,1);
break;
case HAL_TRANSFORM_ROT_180:
glTranslatef(1,1,0);
glRotatef(-180,0,0,1);
break;
case HAL_TRANSFORM_ROT_270:
glTranslatef(1,0,0);
glRotatef(-270,0,0,1);
break;
}
if(texture.NPOTAdjust){
//缩放处理。
glScalef(texture.wScale,texture.hScale,1.0 f);
}
//使能纹理坐标
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//设置顶点坐标。
glVertexPointer(2,GL_FIXED,0,mVertices);
//设置纹理坐标。
glTexCoordPointer(2,GL_FIXED,0,texCoords);
while(it!=end){
const Rect&r=*it++;
const GLint sy=fbHeight-(r.top+r.height());
//裁剪。
glScissor(r.left,sy,r.width(),r.height());
//画矩形。
glDrawArrays(GL_TRIANGLE_FAN,0,4);
}
//禁止纹理坐标。
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
纹理绑定是OpenGL的常用函数,其代码如下所示。
[—>LayerBase.cpp]
void LayerBase:validateTexture(GLint textureName)const
{
//下面这个函数将绑定纹理。
glBindTexture(GL_TEXTURE_2D,textureName);
……//其他一些设置
}
handleRepaint这个函数基本上就是按Z轴的顺序对每一层进行重绘,重绘的方法就是使用OpenGL。
注意 我在Android平台上有几个月的OpenGL开发经历,还谈不上很深刻,不过整理了一些资料,希望能够给感兴趣的读者提供参考。
1)OpenGL的入门教材当选NeHe的资料,大略看前几章即可。
2)Android平台上关于OpenGL ES的开发,有一篇很详细的Word文档叫《OpenGL ES Tutorial for Android》。该文详细地描述了在Android平台上进行OpenGL开发的流程。大家可跟着这篇教材,在模拟器上做一些练习。那里面所涉及的一些基础知识,可从前面介绍的入门教材中学到。
3)有了前面两点的基础后,就需要对整个OpenGL有比较完整深入的了解了。我在那时所看的书是《OpenGL Programming Guide(7th Edition)》。该书很厚,有1000多页。里面有一些内容可能与工作无关,只要大概知道有那回事就行了,暂时不必深入学习,等需要时再进一步学习并运用。我在开发的项目中曾用到的光照、雾化等效果,都是之前先知道有这个东西,后来在项目中才逐渐学习运用的。
4)嵌入式平台上用的其实是OpenGL ES。这里,还有一本书叫《OpenGL ES 2.0 Programming Guide》,它介绍了OpenGL ES的开发,读者可认真学习。
5)在Android SDK文档中,对OpenGL API的描述只有寥寥数语。怎么办?不过,由于它使用了J2ME中的javax.microedition.khronos.opengles包,所以J2ME的SDK文档中对OpenGL的API有着非常详细的描述,读者手头应该要有一个J2ME的文档。
6)如果想做深入开发,就不得不学习计算机图形学了。我后来买了书,可惜没时间学了。
4.unlockClients和postFrameBuffer分析
在绘制完图后,还有两项工作需要做,一个涉及unlockClients函数,另外一个涉及postFrameBuffer函数,这两个函数分别干了什么呢?unlockClients的代码如下所示:
[—>SurfaceFlinger.cpp]
void SurfaceFlinger:unlockClients()
{
const LayerVector&drawingLayers(mDrawingState.layersSortedByZ);
const size_t count=drawingLayers.size();
sp<LayerBase>const*const layers=drawingLayers.array();
for(size_t i=0;i<count;++i){
const sp<LayerBase>&layer=layers[i];
layer->finishPageFlip();
}
}
再看Layer的finishPageFlip函数,代码如下所示:
[—>Layer.cpp]
void Layer:finishPageFlip()
{
//释放FrontBufferIndex。
status_t err=lcblk->unlock(mFrontBufferIndex);
}
原来,unlockClients会释放之前占着的FrontBuffer的索引号。下面看最后一个函数postFrameBuffer,代码如下所示:
[—>SurfaceFlinger.cpp]
void SurfaceFlinger:postFramebuffer()
{
if(!mInvalidRegion.isEmpty()){
const DisplayHardware&hw(graphicPlane(0).displayHardware());
const nsecs_t now=systemTime();
mDebugInSwapBuffers=now;
//调用这个函数后,混合后的图像就会传递到屏幕中显示了。
hw.flip(mInvalidRegion);
mLastSwapBufferTime=systemTime()-now;
mDebugInSwapBuffers=0;
mInvalidRegion.clear();
}
}
flip将调用在DisplayHardware一节中提到的eglSwapBuffer函数,以完成FrameBuffer的PageFlip操作,代码如下所示:
[—>DisplayHardware.cpp]
void DisplayHardware:flip(const Region&dirty)const
{
checkGLErrors();
EGLDisplay dpy=mDisplay;
EGLSurface surface=mSurface;
……
if(mFlags&PARTIAL_UPDATES){
mNativeWindow->setUpdateRectangle(dirty.getBounds());
}
mPageFlipCount++;
eglSwapBuffers(dpy,surface);//PageFlipping,此后图像终于显示在屏幕上了!
}