GUI系统之SurfaceFlinger(15)handlePageFlip

时间:2021-07-20 14:25:24
文章都是通过阅读源码分析出来的,还在不断完善与改进中,其中难免有些地方理解得不对,欢迎大家批评指正。
转载请注明:From LXS. http://blog.csdn.net/uiop78uiop78/

GUI系统之SurfaceFlinger章节目录:

blog.csdn.net/uiop78uiop78/article/details/8954508


1.1.1 handlePageFlip

PageFlip可以理解为“翻页”。从这个意思上来看,它应该与图层缓冲区有关系——因为是多缓冲机制,在适当的时机,我们就需要做“翻页”的动作。

void SurfaceFlinger::handlePageFlip()

{…

    const DisplayHardware&hw = graphicPlane(0).displayHardware();//编号为0的Display

    const RegionscreenRegion(hw.bounds());//整个屏幕区域

const LayerVector&currentLayers(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等等

 

 GUI系统之SurfaceFlinger(15)handlePageFlip

图 11‑38 lockPageFlip流程图

 

Step2@SurfaceFlinger::handlePageFlip,计算所有layer的可见区域。在分析源码前,我们自己先来想一下,图层中什么样的区域是可见的呢?

l  Z-order

各layer的z-order无疑是第一考虑的要素。因为排在越前面的图层,其获得曝光的机率越大,可见的区域也可能越大,如下图所示:


GUI系统之SurfaceFlinger(15)handlePageFlip

图 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中,然后不断地累积运算,直到覆盖整个屏幕区域

GUI系统之SurfaceFlinger(15)handlePageFlip

图 11‑40 Z-order最大的layer可见区域示意图

   如上图所示,外围加深部分是这个图层的尺寸大小,中间挖空区域则是完全透明的,因而需要被剔除。半透明区域比较特殊,它既属于上一个图层的可见区域,又不被列为遮盖区域

 

Ø  对于Z-order不是最大的layer,它首先要计算自身所占区域扣除aboveCoveredLayers后所剩的空间。然后才能像上一步一样再去掉完全透明的区域,这样得到的结果就是它最终的可见区域

 

GUI系统之SurfaceFlinger(15)handlePageFlip

图 11‑41 其它layer的可见区域

 

接下来看源码实现:

void SurfaceFlinger::computeVisibleRegions(const LayerVector&currentLayers,

                                     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来完成。因为图层“脏”区域的坐标空间和屏幕坐标不是同一个,在这里要对其做下坐标变换。