Android 4.1 Surface系统变化说明

jopen 12年前
   <div>     <div>     时间真的是很巧,本来没打算写Surface系统的(相比AudioFlinger来说,Surface变化之后的难度真的是毛毛雨了),但为了庆祝泷泽萝拉发第二部大作,我决定还是要坚持一下。     </div>     <div>     下面将延续Audio的分析风格,从几个层面来介绍Surface系统的变化(JB号称在Surface这块做过大量的优质的改进,无非就是引入在PC机 上早都烂熟的VSYNC,Triple Buffering。但是JB,您能确保这套机制在单核机器上跑得开么?Win Phone 单核,都比多核Android机器流畅。恐怕还是Android上层Display架构有问题吧??!)     </div>     <div>     同Audio一样,想真正理解Surface系统工作原理,最好了解它的演化历史。      <strong><span style="color:#ff0000;">《深入理解Android 卷I》第8章已经把2.2的Surface系统从上到下都撸过一遍了。那帮整天吵嚷着因为大片里边有马赛克而不爽的屌丝们,你们是否把书里因为不懂而产生的”马赛克“搞清楚了??</span></strong>     </div>     <div>     <strong>一 Surface工作流程</strong>     </div>     <div>     <span style="color:#000000;">先说一下Surface的工作流程(只在Native层):</span>     </div>     <ul>      <li>Java层Surface的创建将导致JNI的Surface_init函数的调用。这个Surface还不是APP使用的Surface,中间会经历过乾坤大挪移的过程,请参考上面所提的书籍,写得非常详细。</li>      <li>Surface_writeToParcel,将Surface传递到APP所在进程去使用。</li>      <li>APP所在进程通过Surface_readFromParcel,还原一个Surface对象。此时,你的APP就有脸了。</li>      <li>APP调用Surface_lockCanvas获得一块画布,APP然后在这块画布上作画。</li>      <li>APP调用Surface_unlockCanvasAndPost,将数据推向SurfaceFlinger,完成此次作画。</li>     </ul>     <div>     上面这个流程,在JB中,是没有变化的。从2.2一直到4.1,都是这个流程。     </div>     <div>     这块流程变化基本没有,大家可以直接杀进去看看。     </div>     <div>     <span style="color:#000000;"><strong>二 SurfaceFlinger变化说明</strong></span>     </div>     <div>     <strong>3.1 SF成员变化说明</strong>     </div>     <div>     SF变化很大,主要是它的兄弟们变化较大。我们分别来说,先看图1。     </div>     <div>     <img alt="Android 4.1 Surface系统变化说明" src="https://simg.open-open.com/show/2bbea8d9c87949b4a3ab176452b083ef.png" width="462" height="338" />      <br />     </div>     <div>     图1 DisplayHardware和兄弟们     </div>     <div>     图1的简单说明如下:     </div>     <ul>      <li>       <div>       JB为了支持VSYNC(不懂的同学们,参考这篇文章        <a href="/misc/goto?guid=4958348675984832311" rel="nofollow">http://blog.sina.com.cn/s/blog_4a3946360100wjoo.html</a>), 修改了DisplayHardware和HardwareComposer(这个鬼类,其实是3.0出现的,)。简单来说,VSYNC就是一个同步事件。 同步嘛,都是到这个点了,大家把状态对一下。就好像美国大片那些特工们,干某些事情前总要晃晃手表,对下时间一样。至于拿到这个同步事件后到底去干什么, 以后看代码就知道了。       </div> </li>      <li>       <div>       VSYNC原则上显示芯片(以后简称显卡)提供的。但JB走得太快了,很多硬件或者没支持,或者接口上没支持。所以,不能保证每台JB手机都有来自硬件的 VSYNC事件。咋整?终于,JB干出了一个VSyncThread,它是一个线程,用来定时回调以模拟硬件VSYNC事件(这个时间必须比较准确,所以 线程优先级必须比较高)。看,这又是一个铁证,说明JB不适合在单核CPU上跑(我觉得双核也可能不太适合)。       </div> </li>      <li>       <div>       为了支持硬件VSYNC事件,HardwareComposer也增加了相应接口。       </div> </li>      <li>       <div>       DisplayHardware同时从DisplayHardwareBase和HardwareComposer的内部类EventHandler派 生。从EventHandler派生无非是想基础它的onVsyncReceived函数。这个函数将在到VSYNC事件时被调用。       </div> </li>      <li>       <div>       由于层层的封装(就和国内工程层层转包一样),DisplayHardware内部又定义了一个VsyncHandler虚类,希望人家去继承它的 onVsyncReceived函数。由于使用DH的是SF,所以,大家可大胆猜测这个onVsyncReceived会由SF这一层去处理了。如果去看 代码的话,屌丝们会发现DisplayHardware实现的onVsyncReceived函数其实没干什么有毛意义的事情,就是去调用某个实现了 VsyncHandler对象的onVsyncReceived函数。(有点绕口令吧?再仔细看看!)       </div> </li>     </ul>     <div>     再来看图2:     </div>     <div>     <img style="width:600px;height:456px;" alt="Android 4.1 Surface系统变化说明" src="https://simg.open-open.com/show/ac7d73685ef51ae9778d6089e258c7d2.png" />      <br />     </div>     <div>     图2 SF和它的兄弟们     </div>     <div>     图2解释如下:     </div>     <ul>      <li>       <div>       先看左上部分。对,你没看错。这里也有MessageHandler,Looper,MessageBase。这以前仅是Java层的东西(所以说,原理 是相通的,语言只是工具,如果你懂Java层Message相关的知识,这里又有神马理由说不懂呢?除非你没真正理解Java层Message的东西,参 考这篇博客吧        <a href="/misc/goto?guid=4958348676780903462" rel="nofollow">http://blog.csdn.net/innost/article/details/6055793</a>)。 注意其中的MessageBase,它是一个虚类,一方面它实现了父类的handleMessage函数,另一方面需要子类实现handle函数。这一点 和Java层的Message不一样。所以,当你在SF中发现有地方往MessageQueue抛消息的时候,就不要去找Handler了,先直接看看这 个消息的handle函数!       </div> </li>      <li>       <div>       SF保存了一个MessageQueue对象,作为显示的服务端,它也采取了消息队列的方式来驱动自己工作。这种方式在4.0中已经这么做了,但还不够完 全,不够彻底。JB中,SF的threadLoop函数就一句话:waitForEvent()。对于这种改变,我只能说:很好,很强大!       </div> </li>      <li>       <div>       SF保存了一个EventThread对象,这又是一个线程类,它从VsyncHandler派生,实现了onVsyncReceived函数。       </div> </li>      <li>       <div>       SF新增IDisplayEventConnection跨binder接口,数据通道是BitTube(MD,Tube的意思就是pipe。无语了。开 发SF的人一定和开发AF的人坐得很远很远..)。这个类的作用就是收集底层的VSYNC事件,然后派发给各个Connection。大胆猜测下,是不是 有些APP所需要的FPS不同,所以需要先由DisplayHardware提供一个最小单位的VSYNC时间,然后再由EventThread根据应用 申请的VSYNC时间去触发。类似时钟分频嘛!       </div> </li>     </ul>     <div>     <span style="color:#ff0000;"><strong><em>大胆猜测,小心求证</em></strong></span>。恩,希望你们掌握这个方法。     </div>     <div>     <strong>3.2 createSurface变化说明</strong>     </div>     <div>     从流程上说,这个函数并没有变化,有变化的还是那几个兄弟。见图3,这里只讨论NormalSurface的情况:     </div>     <div>     <img style="width:631px;height:441px;" alt="Android 4.1 Surface系统变化说明" src="https://simg.open-open.com/show/793348ca90db89180a59151ebbc88d41.png" />      <br />     </div>     <div>     图3 Layer和SurfaceTexture的关系     </div>     <div>     稍加解释:     </div>     <ul>      <li>       <div>       图3左边三个是Layer的派生关系。Layer是什么?Layer是SF中代表每个显示层的东西,里边处理了绘画等众多逻辑。       </div> </li>      <li>       <div>       右边是ISurfaceTexture。它其实是Android Native显示层的接口,其作用类似AudioTrack和AudioRecord。以后偶会给一个例子,就是直接使用ISurfaceTexture API绘画的。相比其他版本,JB在SurfaceTexure这一块又抽象出了BufferQueue一层。我个人觉得是越搞越复杂了。 BufferQueue,再处理PageFlip的时候,可能有些好处吧。       </div> </li>      <li>       <div>       客户端调用getSurfaceTexture的时候,在Layer的createSurface中,将返回SurfaceTextureLayer给客 户端。客户端然后调用requestBuffer,queueBuffer,dequeueBuffer获取GraphicBuffer(简单认为为显存 吧)。       </div> </li>      <li>       <div>       ISurfaceTexture内部定义了两个POD结构体,QueueBufferInput/Output,POD结构体,其实就是一个没有什么 static成员的struct。这两个结构体内部包含一些诸如宽度,高度,时间戳方面的信息,将做为queueBuffer函数的参数传递。具体作用, 没细看。       </div> </li>     </ul>     <div>     折腾来,折腾去,无非是搞得更复杂了.....要是能看到相关设计文档就好了....够我们学一阵子了。     </div>     <div></div>     <div>     <strong>3.3 dequeueBuffer和queueBuffer变化说明</strong>     </div>     <ul>      <li>       <div>       dequeueBuffer:取空闲显卡内存,以给app作画。如果编译的时候打开了TARGET_DISABLE_TRIPLE_BUFFERING, 则显卡内存默认是2块,否则是3块。相关queue/dequeue操作的代码,早在2.2的时候就不是仅支持2块的。也就是说,代码里边其实是可以支持 3,4,5等等。不就是一个数组来回倒腾么,当然不会蠢到写死为2!这部分逻辑相比ICS,没有太大的变化。只是把代码挪到BufferQueue里边来 了。       </div> </li>     </ul>     <div>     下面来看看图4,即queueBuffer函数的流程图。这部分变化比较大。     </div>     <div>     <img style="width:568px;height:203px;" alt="Android 4.1 Surface系统变化说明" src="https://simg.open-open.com/show/c7d04fc8cbe829e170b01d513062f0ef.png" />      <br />     </div>     <div></div>     <div>     图4 queueBuffer的流程     </div>     <div></div>     <div>     整个这么复杂的流程,其实从第4步开始,基本上都是一个简单的回调。这就是层次太多带来的负面作用。     </div>     <div>     根据图1,最后调用的是EventThread的requestNextSync,这个函数巨简单,就是触发一个同步广播对象.....     </div>     <div>     还有很多知识点,但不能放在这一节里扯了。下面,我们来看SurfaceFlinger和EventThread的关系。     </div>     <div></div>     <div>     <strong>3.4 SurfaceFlinger和EventThread的工作流程说明</strong>     </div>     <div>     上一节,有个地方没法说明的就是EventThread到底是干嘛的,它和SF分别是两个线程(MD,又是多线程编程,同步很重要啊!)     </div>     <div>     在SF的readyToRun函数中,将通过MesssageQueue的setEventThread函数建立SF和ET的关系。看看代码吧:     </div>     <div>      <div>      <span style="background-color:#ffd700;">void MessageQueue::setEventThread(const sp       <eventthread>        & eventThread)       </eventthread></span>      </div>      <div>      <span style="background-color:#ffd700;">{</span>      </div>      <div>      <span style="background-color:#ffd700;">mEventThread = eventThread;</span>      </div>      <div>      <span style="background-color:#ffd700;">mEvents = eventThread->createEventConnection();</span>      </div>      <div>      <span style="background-color:#ffd700;">mEventTube = mEvents->getDataChannel();</span>      </div>      <div>      <span style="background-color:#ffd700;">mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT,</span>      </div>      <div>      <span style="background-color:#ffd700;">MessageQueue::cb_eventReceiver, this);</span>      </div>      <div>      <span style="background-color:#ffd700;">}</span>      </div>     </div>     <div>     也就是说,当EventThread有什么鸟事的时候,都会通过cb_eventReceiver回调到SF线程。     </div>     <div>     <span style="color:#ff0000;">注意:JB以前,Android老是喜欢用pipe作为进程间通信的手段,现在改成 socketpair了。这是一个很大的变化。所以,会网络编程的同学可以happy一下了。不会的淫,也无所谓,反正是把socket当pipe来使, 都是IPC通信手段,你管它用得是什么呢!</span>     </div>     <div>     EventThread的requestSync被调用后,会触发EventThread的线程通过Connection发送一个消息给SF所在的线程, 即刚才那个cb_eventReceiver被调用,图4是此后的工作流程。(SF系统现在变得也是磨磨唧唧了,不懂POSIX编程屌丝们,你们无论如何 得花点功夫研究了。)     </div>     <div>     <img alt="Android 4.1 Surface系统变化说明" src="https://simg.open-open.com/show/f735f1b4491d12e1d32c3983bdd30f9d.png" width="570" height="504" />      <br />     </div>     <div>     图5 SF工作流程     </div>     <div>     正如图5最后的第10,11步,终于看到了熟悉的handlePageFlip。FT,相比ICS的版本,函数是越调越多,层次越来越复杂。到时候别搞得和Java Framework一样,那效率就.....     </div>     <div></div>     <div>     <strong>四总结</strong>     </div>     <div>     相比AF来说,SF主要是层次增加,函数调用绕来绕去。另外,新增了IDisplayEventConnection接口,这玩意儿还有待后续研究。今天讲得这些东西,应该可以帮助大家对JB SF有一个大略的了解。      <br />      <br /> 来自:http://my.oschina.net/innost/blog/67739     </div>    </div>