文章都是通过阅读源码分析出来的,还在不断完善与改进中,其中难免有些地方理解得不对,欢迎大家批评指正。
转载请注明:From LXS. http://blog.csdn.net/uiop78uiop78/ GUI系统之SurfaceFlinger章节目录: blog.csdn.net/uiop78uiop78/article/details/8954508 1.1.1 handlePageFlipPageFlip可以理解为“翻页”。从这个意思上来看,它应该与图层缓冲区有关系——因为是多缓冲机制,在适当的时机,我们就需要做“翻页”的动作。 void SurfaceFlinger::handlePageFlip() {… const DisplayHardware&hw = graphicPlane(0).displayHardware();//编号为0的Display const RegionscreenRegion(hw.bounds());//整个屏幕区域 const LayerVector¤tLayers(mDrawingState.layersSortedByZ);/*当前所有layers*/
const bool visibleRegions = lockPageFlip(currentLayers);/*Step1.下面会详细分析这个函数,注意它的返回 值是一个bool类型变量。*/ if (visibleRegions ||mVisibleRegionsDirty) {//可见区域发生变化 RegionopaqueRegion;//不透明区域 computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);/*Step2.计算可见区域*/
/*Step3.重建mVisibleLayersSortedByZ,即所有可见图层的排序*/ const size_t count= currentLayers.size(); mVisibleLayersSortedByZ.clear();//清空 mVisibleLayersSortedByZ.setCapacity(count);//容量 for (size_t i=0 ;i<count ; i++) { if(!currentLayers[i]->visibleRegionScreen.isEmpty())//当前图层有可见区域 mVisibleLayersSortedByZ.add(currentLayers[i]); } mWormholeRegion = screenRegion.subtract(opaqueRegion);/*Step4.虫洞计算*/ mVisibleRegionsDirty = false; invalidateHwcGeometry(); } unlockPageFlip(currentLayers);/*Step5.与lockPageFlip相对应 */ … mDirtyRegion.andSelf(screenRegion);//排除屏幕范围之外的区域 } Step1@SurfaceFlinger::handlePageFlip,通过lockPageFlip分别锁定各layer当前要处理的缓冲区。SurfaceFlinger::lockPageFlip逻辑比较简单,我们直接来看下Layer::lockPageFlip: void Layer::lockPageFlip(bool& recomputeVisibleRegions) {… if (mQueuedFrames > 0) {… if(android_atomic_dec(&mQueuedFrames) > 1) { mFlinger->signalLayerUpdate(); } … Rejectr(mDrawingState, currentState(), recomputeVisibleRegions); if (mSurfaceTexture->updateTexImage(&r) < NO_ERROR) {//加载新的纹理 recomputeVisibleRegions = true; return; } mActiveBuffer = mSurfaceTexture->getCurrentBuffer();//当前活跃的缓冲区 …//其它内部变量更新 glTexParameterx(GL_TEXTURE_EXTERNAL_OES,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);//配置参数 glTexParameterx(GL_TEXTURE_EXTERNAL_OES,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } } SurfaceTexture设置了一个FrameQueuedListener,当BufferQueue中的一个BufferSlot被queued后,它首先通知这个Listener,进而调用所属Layer的onFrameQueued。这个函数会增加mQueuedFrames的计数,并且向SurfaceFlinger发出一个invalidate信号(signalLayerUpdate)。 因而如果当前没有任何queuedbuffer的话,lockPageFlip()什么都不用做。假如当前有多个mQueuedFrames的话,除了正常处理外,我们还需要另外调用signalLayerUpdate来发出一个新的event。 Layer中持有一个SurfaceTexture对象(成员变量mSurfaceTexture),用于管理BufferQueue(成员变量mBufferQueue)。一旦SurfaceFlinger需要对某个buffer进行操作时,根据前几个小节对BufferQueue状态迁移的分析,我们知道它首先要accquire它,这个动作被封装在SurfaceTexture::updateTexImage中。除此之外,这个函数还需要根据Buffer中的内容来更新Texture,稍后我们做详细分析。 在lockPageFlip内部,定义了一个Reject结构体,并做为函数参数传递给updateTexImage。后者在acquire到buffer后,调用Reject::reject()来判断这个缓冲区是否符合要求。如果updateTexImage成功的话,Layer就可以更新mActiveBuffer,也就是当前活跃的缓冲区(每个layer对应32个BufferSlot);以及其它一系列相关内部成员变量,比如mCurrentCrop、mCurrentTransform等等,这部分源码省略。 纹理贴图还涉及到很多细节的配置,比如说纹理图像与目标的尺寸大小有可能不一致,这种情况下怎么处理?或者当纹理坐标超过[0.0,1.0]范围时,应该怎么解决。这些具体的处理方式都可以通过调用glTexParameterx(GLenumtarget, GLenum pname, GLfixed param)来配置。函数的第二个参数是需要配置的对象类型(比如GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T分别代表两个坐标维);第三个就是具体的配置。 status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {… Mutex::Autolocklock(mMutex); status_t err = NO_ERROR; … EGLDisplay dpy =eglGetCurrentDisplay();//当前Display EGLContext ctx =eglGetCurrentContext();//当前环境Context … BufferQueue::BufferItemitem; err = mBufferQueue->acquireBuffer(&item);//acquire当前需要处理的buffer,封装在item中 if (err == NO_ERROR) {//acquire成功 int buf = item.mBuf; //此Buffer在Slot中的序号 if(item.mGraphicBuffer != NULL) {… mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;//后面会用到 } … EGLImageKHR image =mEGLSlots[buf].mEglImage; if (image ==EGL_NO_IMAGE_KHR) {/*假如image不为空的话,前面会先把它destroy掉,代码 省略*/ if(mEGLSlots[buf].mGraphicBuffer == NULL) { …//异常处理 } else { image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer);//生成一个纹理图像 mEGLSlots[buf].mEglImage = image; … } }
if (err == NO_ERROR) {… glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); … }
if (err != NO_ERROR) {//将acquired的buffer释放 mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR; return err; } … /*最后更新SurfaceTexture中各状态*/ mCurrentTexture = buf; mCurrentTextureBuf =mEGLSlots[buf].mGraphicBuffer; … } else {… } return err; } 这个函数比较长,我们只留下了最核心的部分。它的目标是更新Layer的纹理(Texture),分为如下几步: ①Acquire一个需要处理的Buffer。根据前面对Buffer状态迁移的分析,当消费者想处理一块buffer时,它首先要向BufferQueue做acquire申请。那么BufferQueue怎么知道当前要处理哪一个Buffer呢?这是因为其内部维护有一个Fifo先入先出队列。一旦有buffer被enqueue后,就会压入队尾;每次acquire就从队头取最前面的元素进行处理,完成之后就将其从队列移除 ②Acquire到的buffer封装在BufferItem中,item.mBuf代表它在BufferSlot中的序号。正常情况下item.mGraphicBuffer都不为空,我们将它记录到mEGLSlots[buf].mGraphicBuffer中,以便后续操作。假如mEGLSlots[buf].mEglImage当前不为空(EGL_NO_IMAGE_KHR)的话,需要先把旧的image销毁(eglDestroyImageKHR) ③SurfaceFlinger有权决定SurfaceTexture得到的Buffer是否有效合法(比如说size是否正确),这是通过updateTexImage(BufferRejecter* rejecter)中的rejecter对象来完成的。BufferRejecter实现了一个reject接口,用于SurfaceTexture调用来验证前面acquire到的buffer是否符合要求 ④接下来就生成Texture了。需要分别调用glBindTexture和 glEGLImageTargetTexture2DOES。后一个函数是opengl es中对glTexImage2D的扩展,因为在嵌入式环境下如果直接采用这个API的话,当图片很大时会严重影响到速度,而经过扩展后的glEGLImageTargetTexture2DOES可以解决这个问题。这个接口是和eglCreateImageKHR配套使用的,封装在前面createImage中。不了解这些函数用法的读者请务必参考opengles技术文档 ⑤消费者一旦处理完Buffer后,就可以将其release了。此后这个buffer就又恢复FREE状态,以供生产者再次dequeue使用 ⑥最后,我们需要更新SurfaceTexture中的各成员变量,包括mCurrentTexture、mCurrentTextureBuf、mCurrentCrop等等
图 11?38 lockPageFlip流程图
Step2@SurfaceFlinger::handlePageFlip,计算所有layer的可见区域。在分析源码前,我们自己先来想一下,图层中什么样的区域是可见的呢? l Z-order 各layer的z-order无疑是第一考虑的要素。因为排在越前面的图层,其获得曝光的机率越大,可见的区域也可能越大,如下图所示: 图 11?39 后面的图层有可能被遮挡而不可见
所以在计算可见性时,是按照Z-order由上而下进行的。假如一个layer的某个区域被确定为可见,那么与之相对应的它下面的所有图层区域都会被遮盖而不可见 l 透明度 虽然越前面的layer优先级越高,但这并不代表后面的图层完全没有机会。只要前一个layer不是完全不透明的,那么从理论上来讲用户就应该能“透过”这部分区域看到后面的内容 l 图层大小 与透明度一样,图层大小也直接影响到其可见区域。因为每个layer都是有大有小的,即便前一个layer是完全不透明的,但只要它的尺寸没有达到“满屏”,那么比它z-order小的图层还是有机会暴露出来的。这也是我们需要考虑的因素之一
综合上面的这几点分析,我们能大概制定出计算layer可见区域的逻辑步骤: 按照Z-order逐个计算各layer的可见区域,结果记录在LayerBase::visibleRegionScreen中 对于Z-order值最大的layer,显然没有其它图层会遮盖它。所以它的可见区域(visibleRegion)应该是(当然,前提是这个layer没有超过屏幕区域)自身的大小再减去完全透明的部分(transparentRegionScreen),由此计算出来的结果我们把它称为aboveCoveredLayers。这个变量应该是全局的,因为它需要被传递到后面的layers中,然后不断地累积运算,直到覆盖整个屏幕区域 图 11?40 Z-order最大的layer可见区域示意图 如上图所示,外围加深部分是这个图层的尺寸大小,中间挖空区域则是完全透明的,因而需要被剔除。半透明区域比较特殊,它既属于上一个图层的可见区域,又不被列为遮盖区域
对于Z-order不是最大的layer,它首先要计算自身所占区域扣除aboveCoveredLayers后所剩的空间。然后才能像上一步一样再去掉完全透明的区域,这样得到的结果就是它最终的可见区域 图 11?41 其它layer的可见区域
接下来看源码实现: void SurfaceFlinger::computeVisibleRegions(const LayerVector¤tLayers, Region& dirtyRegion, Region& opaqueRegion) {… const GraphicPlane&plane(graphicPlane(0)); const Transform&planeTransform(plane.transform()); const DisplayHardware&hw(plane.displayHardware()); const RegionscreenRegion(hw.bounds());//整个屏幕区域
Region aboveOpaqueLayers; Region aboveCoveredLayers;//全局的,用于描述当前已经被覆盖的区域 Region dirty;
bool secureFrameBuffer =false; size_t i =currentLayers.size();//所有layer数量 while (i--) {/Step1. 注意计算的方向,按照Z-order由大到小的顺序 constsp<LayerBase>& layer = currentLayers[i];//取得这一layer layer->validateVisibility(planeTransform);//验证当前layer的可见性,大家可以自行分析 constLayer::State& s(layer->drawingState());//layer的状态 Region opaqueRegion;//完全不透明的区域 Region visibleRegion;//可见区域 Region coveredRegion;//被遮盖的区域。以上三个变量都是局部的,用于描述各个layer if(CC_LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)){ /*Step2. 这部分计算可见区域visibleRegion和完全不透明区域opaqueRegion*/ const booltranslucent = !layer->isOpaque(); //layer可见,又不是完全不透明,那就是半透明 const Rectbounds(layer->visibleBounds()); visibleRegion.set(bounds);//可见区域的初始值 visibleRegion.andSelf(screenRegion);//和屏幕大小先进行与运算 if(!visibleRegion.isEmpty()) {//如果经过上述运算,可见区域还不为空的话 if(translucent) { //将完全透明区域从可见区域中移除 visibleRegion.subtractSelf(layer->transparentRegionScreen); } const int32_tlayerOrientation = layer->getOrientation();//旋转角度 if(s.alpha==255 && !translucent && ((layerOrientation & Transform::ROT_INVALID) == false)) { // theopaque region is the layer's footprint opaqueRegion = visibleRegion;//完全一致 } } }
/*Step3. 考虑被上层layer覆盖区域*/ coveredRegion = aboveCoveredLayers.intersect(visibleRegion);//计算会被遮盖的区域 aboveCoveredLayers.orSelf(visibleRegion);//累加计算,然后传给后面的layer visibleRegion.subtractSelf(aboveOpaqueLayers);//扣除被遮盖的区域
/*Step4. 计算“脏”区域*/ if(layer->contentDirty) {//当前layer有脏内容 dirty =visibleRegion;//不光要包括本次的可见区域 dirty.orSelf(layer->visibleRegionScreen);//还应考虑上一次没处理的可见区域 layer->contentDirty = false;//处理完成 } else {//当前layer没有脏内容 const RegionnewExposed = visibleRegion - coveredRegion; const RegionoldVisibleRegion = layer->visibleRegionScreen; const RegionoldCoveredRegion = layer->coveredRegionScreen; const RegionoldExposed = oldVisibleRegion - oldCoveredRegion; dirty =(visibleRegion&oldCoveredRegion) | (newExposed-oldExposed); } /*Step5. 更新各变量*/ dirty.subtractSelf(aboveOpaqueLayers);//扣除不透明区域 dirtyRegion.orSelf(dirty);//累积计算脏区域 aboveOpaqueLayers.orSelf(opaqueRegion);//累积不透明区域 layer->setVisibleRegion(visibleRegion);//设置layer内部的可见区域 layer->setCoveredRegion(coveredRegion);//设置layer内部的被遮盖区域
/*Step6. 屏幕截图保护*/ if(layer->isSecure() && !visibleRegion.isEmpty()) { secureFrameBuffer= true; } }//while循环结束
/*Step7. 考虑被移除layer占据的区域*/ dirtyRegion.orSelf(mDirtyRegionRemovedLayer); mDirtyRegionRemovedLayer.clear(); mSecureFrameBuffer =secureFrameBuffer; opaqueRegion =aboveOpaqueLayers; } Step1@ SurfaceFlinger::computeVisibleRegions,循环处理每个layer。注意计算方向(i--)采取Z-order由大而小的顺序,这和我们之前的分析是一致的。这个函数中定义了很多变量,包括对各layer全局的aboveOpaqueLayers、aboveCoveredLayers、dirty,以及局部的opaqueRegion、visibleRegion、coveredRegion,但基本逻辑还是和我们前面的推测相符合的。 Step2@ SurfaceFlinger::computeVisibleRegions,计算visibleRegion和opaqueRegion。不过有两个情况下不需要这么做。其一就是当前layer被隐藏了,这可以从s.flags & ISurfaceComposer::eLayerHidden中获得;其二就是它的alpha值为0,也就是说当前layer是完全透明的。 如果不是这两种情况的话,就可以继续计算visibleRegion。它的初始值是layer->visibleBounds(),相当于前面我们说的该layer所占据的区域。这一初始值与屏幕区域进行与运算,排除屏幕显示区域外的部分。此时如果visibleRegion还有剩余空间的话,就继续减掉透明区域。 变量opaqueRegion在某些情况下和visibleRegion是一样的。即当前图层完全不透明(alpha==255&&!translucent)且旋转角度值为ROT_INVALID时,两者可认为是同一个区域。 Step3@ SurfaceFlinger::computeVisibleRegions,考虑被上层layer遮盖的情况。其中coveredRegion计算得到会被遮盖的区域,即visibleRegion需要剔除的部分。并且累加更新全局的aboveCoveredLayers,以便后面的layer可以继续使用。 Step4@ SurfaceFlinger::computeVisibleRegions,在前面的基础上进一步计算dirty区域,也就是需要渲染的部分。涉及到两个变量,dirty是一个全部变量,它表示每个layer中的脏区域;而dirtyRegion是函数的出参,属于全局性的,用于计算所有layer脏区域的集合(采用“或”运算)。可能有人会问,需要渲染的区域不就是可见区域吗?这可不一定。比如说当前界面内容没有任何改变,那么为什么还要浪费时间再重新渲染一次呢? 如果有“脏”内容(layer->contentDirty为true),dirty要同时覆盖当前可见区域(visibleRegion),以及上次还未考虑的可见区域(layer->visibleRegionScreen)。 如果没有“脏”内容,那么我们只要渲染这次新“暴露”出来的区域就可以了。因为上一次暴露出来的区域已经被渲染过了,而且内部又没有变化,当然不需要再次渲染。 Step5@ SurfaceFlinger::computeVisibleRegions,更新所有相关变量,包括layer内部的visibleRegionScreen(通过setVisibleRegion)、coveredRegionScreen(通过setCoveredRegion),以及需要累积计算的aboveOpaqueLayers,和dirtyRegion等等。 Step6@ SurfaceFlinger::computeVisibleRegions,判断当前窗口内容是否受保护(变量secureFrameBuffer)。判断的标准就是当前可见区域不为空(!visibleRegion.isEmpty()),且需要安全保护(isSecure()),这将影响到截屏功能是否可以正常执行。 Step7@ SurfaceFlinger::computeVisibleRegions,除了上述的步骤外,我们还要考虑那些被移除的layer所占据的区域,这样得到的才是最终的结果。函数出参opaqueRegion也就是通过各layer计算后的累加值aboveOpaqueLayers。
分析完可见区域的计算后,我们回到本小节的handlePageFlip函数中。 Step3@SurfaceFlinger::handlePageFlip. 通过前面的computeVisibleRegions,现在所有layer的可见区域都已经记录在其内部的visibleRegionScreen中了。接下来 mVisibleLayersSortedByZ用于对所有可见的图层进行z-order排序。 Step4@SurfaceFlinger::handlePageFlip. 在computeVisibleRegions中,opaqueRegion做为所有layer的遮挡区域(对应aboveOpaqueLayers)的累积结果,正常情况下应该是和屏幕大小是一致的。反之,如果它们不一样的话,就产生了wormhole区域: mWormholeRegion = screenRegion.subtract(opaqueRegion); 这部分区域比较特殊,因而在后续的图层合成时需要做额外处理(具体调用drawWormhole来实现)。 Step5@SurfaceFlinger::handlePageFlip,调用unlockPageFlip做最后的准备工作。实现上和lockPageFlip有相似之处,也是通过循环调用各layer自己的unlockPageFlip来完成。因为图层“脏”区域的坐标空间和屏幕坐标不是同一个,在这里要对其做下坐标变换。 |
|
来自: 老匹夫 > 《Graphics》