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