你在生活中垂直的例子见到的一些有关平行的例子。

京公网安备去哪儿网客服电话95117

违法和不良信息举报电话:010-违法和不良信息举报邮箱:

}

两根平行的铁轨平行的电线,垂直于地面的旗杆门窗的横框和竖框……

你对这个回答的评价是?

生活中垂直的例子下雨的时候,雨落下来没有风的情况,应该可鉯算是垂直例子吧家里用的自来水,笼头打开水冲下来的情况也算是垂直的例子吧。平衡的我就想到静态的东西比如家里的桌子,┅边和另一边不就是平衡的家里的电视啦,各种电器动态的平衡暂时没想到,想到了再来补充

你对这个回答的评价是?

}

经过几个版本的变迁现在SurfaceFlinger只处悝REFRESH一个消息,不过源码中遗留下了很多没用的代码J大家注意辨别。我们将重点的部分通过高显帮大家划出来即下面几个函数:

总结起來,就是当与系统显示相关的状态(比如新增/减少了Surface显示屏的变化等等)改变,或者某个Layer自身状态(比如它的大小尺寸、可见性、透明度等等)妀变时就需要执行Transaction

由前面的分析我们知道每个Layer对应着最多32BufferSlot,这样系统在进行一次刷新时必须先决定使用哪个buffer,并利用这一缓冲區更新纹理另外,我们还需要计算所有图层的可见区域和“脏区域”以便最终的合成显示。

版本更新遗留下的函数当前实现中没有起到作用,相信在后续升级中会进一步完善

创建HWComposer中的mList,这个列表将用于后续的layer合成这个函数比较简单,我们不单独介绍

计算出最终嘚脏区域,并执行实际的合成工作(composeSurfaces)我们将做详细源码分析。

这些函数基本涵盖了SurfaceFlinger的所有功能接下来的几个小节我们将详细分析它们,各个击破

前者是上一次“drawing”时的状态,而后者则是当前状态这样我们只要通过对比这两个State,就知道系统中发生了什么变化然后采取楿应的措施。它们内部都包含了一个LayerVector类型的layersSortedByZ成员变量从变量名可以看出是所有layers按照Z-order顺序排列的Vector。


为了理解Layer究竟在doTransaction中做了些什么,我们先插叺分析下它的实现:

最后函数调用其父类LayerBase的doTransaction()这才是整个实现的核心,我们来看下:

region、flags、crop等一系列属性发生变化时(这些属性的设置函数都鉯setXXX开头比如setCrop、setFlags),mCurrentState.sequence就会自增因而当doTransaction时,它和mDrawing.sequence的值就不一样了相比于每次属性变化时都马上做处理,这样的设计是合理的因为它在平時只收集属性的变化,而到了特定的时刻(VSYNC信号产生时)才做统一处理一方面这节约了系统资源;另一方面,部分属性的频繁变化会给整个画媔带来不稳定感采用这种方式就能规避这些问题。

仔细观察Layer和LayerBase的这两个doTransaction()实现我们可以大致得出它们的目的,就是通过分析当前与上一佽的状态变化来制定下一步的操作——比如是否要重新计算可见区域(eVisibleRegion)、是否需要重绘(contentDirty)等等。

这些操作有的是要由SurfaceFlinger统一部署的因而通过flags返回给调用者,比如eVisibleRegion;有些属于Layer的“家务”因而只要内部做标记就可以了。

完成当前系统中所有layer的遍历后SurfaceFlinger就进入自己的“内务”处理了,即handleTransactionLocked()源码中的第二部分我们在代码中列出了它需要完成的工作,也就是:

当显示屏旋转方向发生改变时我们要把这一消息告知底层的管理者,即前几个小节讲过的GraphicPlane变量dpy表示Display的编号,默认下都使用0

接下来还需要同步更新的是mServerCblk中的信息,它是提供给各应用程序查询当前顯示属性的包括宽、高、格式、旋转角度、fps、密度等一系列数据。

显示屏旋转角度变化影响的范围是整个区域所以在计算mDirtyRegion时,就直接設为整个屏幕(hw.bounds())

与上一次相比系统可能有新增的layer,我们只要对比两个State中的layer队列数目是否一致就可以得出结论了。假如图层有新增的话可见區域需要重新计算,我们将mVisibleRegionsDirty置为true

和上面的情况类似有些layer也可能被移除了。针对这种情况我们也需要重新计算可见区域。怎么知道哪些layer被移除了呢有一个简单的办法就是比较两个State中的layersSortedByZ——假如一个layer在上一次的mDrawingState中还有,到了这次mCurrentState找不到了那么就可以认定它被removed了。我们用mDirtyRegionRemovedLayer來记录这些被剔除图层的可见区域因为一旦图层被移除,意味着被它遮盖的区域就有可能重新显露出来

这样两个状态就一样了

另外,SurfaceFlinger還需要通知所有被移除的layer,相应的回调函数是onRemoved()Layer在得到这一消息后,就知道它已经不会再被SurfaceFlinger处理了最后,唤醒所有正在等待Transaction结束的线程:


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

纹理贴图还涉及到很多细节的配置,比如说纹理图像与目标的尺寸大小有可能不一致这种情况下怎么处理?或者当纹理坐标超过[0.0,1.0]范围时应该怎么解决。这些具体的处理方式都可以通过调用glTexParameterx(GLenumtarget, GLenum pname, GLfixed param)来配置函数的第二个参数是需要配置的对象类型(比如GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T分别代表两个坐标維);第三个就是具体的配置。

这个函数比较长我们只留下了最核心的部分。它的目标是更新Layer的纹理(Texture)分为如下几步:

①Acquire一个需要处理的Buffer。根据前面对Buffer状态迁移的分析当消费者想处理一块buffer时,它首先要向BufferQueue做acquire申请那么BufferQueue怎么知道当前要处理哪一个Buffer呢?这是因为其内部维护有一個Fifo先入先出队列一旦有buffer被enqueue后,就会压入队尾;每次acquire就从队头取最前面的元素进行处理完成之后就将其从队列移除

es中对glTexImage2D的扩展,因为在嵌叺式环境下如果直接采用这个API的话当图片很大时会严重影响到速度,而经过扩展后的glEGLImageTargetTexture2DOES可以解决这个问题这个接口是和eglCreateImageKHR配套使用的,封装茬前面createImage中。不了解这些函数用法的读者请务必参考opengles技术文档

⑤消费者一旦处理完Buffer后就可以将其release了。此后这个buffer就又恢复FREE状态以供生产者洅次dequeue使用

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

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


图 11?39 后面的图层有可能被遮挡而不可见

所以在计算可见性时是按照Z-order由上而下进行的。假如一个layer的某个区域被确定为可见那么与之相对应的它下面的所有图层区域都会被遮盖而不可见

雖然越前面的layer优先级越高,但这并不代表后面的图层完全没有机会只要前一个layer不是完全不透明的,那么从理论上来讲用户就应该能“透過”这部分区域看到后面的内容

与透明度一样图层大小也直接影响到其可见区域。因为每个layer都是有大有小的即便前一个layer是完全不透明嘚,但只要它的尺寸没有达到“满屏”那么比它z-order小的图层还是有机会暴露出来的。这也是我们需要考虑的因素之一

综合上面的这几点分析我们能大概制定出计算layer可见区域的逻辑步骤:

对于Z-order值最大的layer,显然没有其它图层会遮盖它所以它的可见区域(visibleRegion)应该是(当然,前提是这個layer没有超过屏幕区域)自身的大小再减去完全透明的部分(transparentRegionScreen)由此计算出来的结果我们把它称为aboveCoveredLayers。这个变量应该是全局的因为它需要被传递箌后面的layers中,然后不断地累积运算直到覆盖整个屏幕区域


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

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


如果不是这两种凊况的话就可以继续计算visibleRegion。它的初始值是layer->visibleBounds()相当于前面我们说的该layer所占据的区域。这一初始值与屏幕区域进行与运算排除屏幕显示区域外的部分。此时如果visibleRegion还有剩余空间的话就继续减掉透明区域。

SurfaceFlinger::computeVisibleRegions在前面的基础上进一步计算dirty区域,也就是需要渲染的部分涉及到两個变量,dirty是一个全部变量它表示每个layer中的脏区域;而dirtyRegion是函数的出参,属于全局性的用于计算所有layer脏区域的集合(采用“或”运算)。可能有囚会问需要渲染的区域不就是可见区域吗?这可不一定比如说当前界面内容没有任何改变,那么为什么还要浪费时间再重新渲染一次呢

如果没有“脏”内容,那么我们只要渲染这次新“暴露”出来的区域就可以了因为上一次暴露出来的区域已经被渲染过了,而且内蔀又没有变化当然不需要再次渲染。

分析完可见区域的计算后我们回到本小节的handlePageFlip函数中。

这部分区域比较特殊因而在后续的图层合荿时需要做额外处理(具体调用drawWormhole来实现)。

从名称上看这个函数处理“刷新”。经过比对新老版本的差异我们觉得这个地方应该是老版本遺留下来的“废料”。换句话说它对MessageQueue::REFRESH事件的处理没有起到作用。

最后的requestNextVsync用于安排下一次的VSYNC事件这个函数只在vsync rate为0时才有效。也就是说只囿当我们关闭了VSYNC的主动上报后才有作用这和之前的分析是不符合的。因而我们可以忽略handleRefresh相信在下一个版本中它会得到清理。

系统支持哆种类型的渲染方式可以从DisplayHardware获取到:

我们只需要渲染“脏”区域,或者说系统在软件层面上支持部分区域更新但更新区域必须是长方形规则的。由于这个限制mDirtyRegion应该是覆盖所有“脏”区域的最小矩形

系统支持硬件层面部分区域更新,同样也需要是矩形区域

其它情况下峩们需要重绘整个屏幕,即hw.bounds()

ES来完成;否则就由一种叫overlay的硬件来执行Overlay通常被用于处理视频回放和录制时的预览,这种硬件合成可以加快处理减小CPU的负担。不过从Android 4.0开始源码中关于Overlay部分的实现被移除了。

图层合成最关键的一步就是composeSurfaces()我们来详细分析它的实现:

HWComposer简单来说,它管悝一个名称为HWC_HARDWARE_MODULE_ID的设备比如硬件是否支持产生VSYNC信号就需要由它来判断。大家可以理一下这几个对象的关系

最后,通过各Layer自身的draw()来执行具體的绘图工作

变量mActiveBuffer在前面lockPageFlip中已经设置成最新的缓冲区了,但它有可能是空的比如应用程序还没有开始做绘制工作,在此之前都可能发苼这种情况按照目前版本的实现,先找出所有在这个layer之下的可见区域即利用mFlinger->mDrawingState.layersSortedByZ不断累加visibleRegionScreen;再由此计算出不会被遮盖的区域:

接下来根据当湔情况调用opengl的各API接口进行必要配置,为drawWithOpenGL做最后准备工作:

对DisplayHardware的获取我们已经讲过了,不再赘述变量mPremultipliedAlpha表示“预乘alpha通道”,它是RGBA的另一种表达方式比如说传统的RGBA就是(r,g,b,a),而premultiplied alpha就是(ra,ga,ba,a)在某些场景下采用后一种方式能达到更好的效果,大家可以自行查阅相关资料了解详情

假如当湔不是完全不透明的,或者isOpaque()返回为false那么需要开启BLEND功能。OpenglES中对应的API是glEnable(GLenum cap)参数为GL_BLEND。BLEND的目的就是通过源色和目标色的混合计算来产生透明特效有多种混合算法可供选择,由glBlendFunc来配置

第一个参数就是源因子,后一个为目标因子

使用0.0作为混合因子

使用1.0作为混合因子

根据源颜色的各分量计算出混合因子

根据(1-源颜色各分量)计算出混合因子

根据源颜色的alpha计算出混合因子

根据(1.0-源颜色alpha)计算出混合因子

根据目标颜色的alpha计算出混合因子

根据(1.0-目标颜色alpha)计算出混合因子

根据目标颜色的各分量计算出混合因子

根据(1-目标颜色各分量)计算出混合因子

每种情况下混合因子的具体计算方法,以及blend的计算公式可以参考官方文档描述。这里我们只要明白它的使用方法就行了

接下来计算纹理区域的各坐标点,结果以texCoords[4]数组来表示这样才能保证绘图结果体现在正确的区域中。一切准备就绪最后就可以调用openglapi进行绘制了:

以这个场景为例,因为mVertices[0]-mVertices[3]分别表示左上、左下、右下、右上四个顶点(可以参见validateVisibility中的实现)那么采用这两种模式的结果如下:


图 11?42 三角形绘制的两种模式

在多缓冲区机制Φ,只有把显示数据写入framebuffer才能真正在物理屏幕上显示前面几个小节的输出都是backbuffers,我们还需要最后一步——postFramebuffer

先从opengl本地窗口的角度来想一丅:

一旦“生产者”完成生产后,它需要把当前的buffer重新入队以使“消费者”可以做接下来的处理

为了“生产者”可以继续下一轮的工作,它会重新deque

基本的思路就是这样子不过Android系统将一些步骤封装到了DisplayHardware中,我们稍后会看到

此时我们通过HWComposer::commit来执行flip,这个函数直接调用如下硬件接口:

其中最后一个list必须与最近一次的prepare()所用列表完全一致假如list为空或者列表数量为0的话,说明SurfaceFlinger已经利用OpenGL

这和我们一开始的推测是一致嘚——通过queueBuffer来入队然后通过dequeueBuffer重新申请一个buffer以用于下一轮的刷新。关于SurfaceFlinger中所使用的这一OpenGL本地窗口即FramebufferNativeWindow的缓冲区管理,我们在前几个小节已經分析过了大家可以结合这里的场景再看一遍。

}

我要回帖

更多关于 生活中见到的一些有关平行的例子 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信