助力软件开发企业降本增效 PHP / java源码系统,只需一次付费,代码终身使用! 广告
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(); constDisplayHardware& hw(graphicPlane(0).displayHardware()); if (LIKELY(hw.canDraw() && !isFrozen())) { //③处理重绘 handleRepaint(); hw.compositionComplete(); //④投递BackBuffer unlockClients(); postFramebuffer(); } else{ unlockClients(); usleep(16667); } returntrue; } ~~~ 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& currentLayers = 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(constLayerVector& currentLayers) { boolrecomputeVisibleRegions = false; size_tcount = 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); } returnrecomputeVisibleRegions; } ~~~ 假设当前的显示层是Layer类型,那么得转到Layer类去看它的lockPageFlip函数,代码如下所示: **Layer.cpp** ~~~ void Layer::lockPageFlip(bool&recomputeVisibleRegions) { //lcblk是SharedBufferServer类型,调用retireAndLock函数将返回FrontBuffer的 //索引号 ssize_tbuf = 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(constLayerVector& currentLayers) { constGraphicPlane& plane(graphicPlane(0)); constTransform& planeTransform(plane.transform()); size_tcount = 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; } ...... constDisplayHardware& hw(graphicPlane(0).displayHardware()); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); uint32_t flags = hw.getFlags(); if((flags & DisplayHardware::SWAP_RECTANGLE) || (flags & DisplayHardware::BUFFER_PRESERVED)) { ......//计算mDirtyRegion } // 在脏区域上进行绘制 composeSurfaces(mDirtyRegion); mDirtyRegion.clear(); } ~~~ 其中,composeSurfaces将不同的显示层内容进行混合,其实就是按Z轴的顺序由里到外依次绘制。当然,最后绘制的数据有可能遮盖前面绘制的数据,代码如下所示: **SurfaceFlinger.cpp** ~~~ void SurfaceFlinger::composeSurfaces(constRegion& dirty) { constSurfaceFlinger& flinger(*this); constLayerVector& drawingLayers(mDrawingState.layersSortedByZ); constsize_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 { intindex = 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 { constDisplayHardware& hw(graphicPlane(0).displayHardware()); constuint32_t fbHeight = hw.getHeight(); constState& 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.0f); } //使能纹理坐标 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(GLinttextureName) const { //下面这个函数将绑定纹理 glBindTexture(GL_TEXTURE_2D, textureName); ......//其他一些设置 } ~~~ handleRepaint这个函数基本上就是按Z轴的顺序对每一层进行重绘,重绘的方法就是使用OpenGL。 我在Android平台上有几个月的OpenGL开发经历,还谈不上很深刻,其中的一些资料,希望能够给感兴趣的读者提供参考。 1)OpenGL的入门教材当选NeHe的资料,大略看前几章即可。 2) Android平台上关于OpenGL ES的开发,有一篇很详细的Word文档叫《OpenGL ESTutorial 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() { constLayerVector& drawingLayers(mDrawingState.layersSortedByZ); constsize_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,此后图像终于显示在屏幕上了! } ~~~