GUI系统之SurfaceFlinger(5)BufferQueue内部原理

时间:2021-08-30 00:59:09

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

GUI系统之SurfaceFlinger章节目录:
blog.csdn.net/uiop78uiop78/article/details/8954508



1.1 BufferQueue详解

上一小节我们已经看到了BufferQueue,它是SurfaceTextureClient实现本地窗口的关键。从逻辑上来推断,BufferQueue应该是驻留在SurfaceFlinger这边的进程中。我们需要进一步解决的疑惑是:

Ø  每个应用程序可以对应几个BufferQueue,它们是一对一、多对一或者是一对多?

Ø  应用程序所需要的绘图空间是由谁分配的?

在音频系统的学习中,我们知道AudioTrack和AudioFlinger是通过共享内存的形式来进行数据传递的,那么显示系统中是否也是类似情况?

Ø  应用程序与SurfaceFlinger如何互斥共享数据区

和在Audio系统中遇到的问题一样,我们面临的是经典的“生产者-消费者”模型。显示系统又是如何协调好这两者间的互斥访问的呢?

1.1.1 BufferQueue的内部原理

先来解析下BufferQueue的内部构造,如下图所示:

GUI系统之SurfaceFlinger(5)BufferQueue内部原理

图 11‑12 BufferQueue内部变量

 

因为BufferQueue是ISurfaceTexture的本地实现,所以它必须重载接口中的各虚函数,比如queueBuffer、requestBuffer、dequeueBuffer等等。另外,这个类的内部有一个非常重要的成员数组,即mSlots[NUM_BUFFER_SLOTS],大家是否还记得前面SurfaceTextureClient类中也有一个一模一样的数组:

class SurfaceTextureClient…{

                BufferSlot  mSlots[NUM_BUFFER_SLOTS];

数组的成员是BufferSlot,其中包含的GraphicBuffer变量(mGraphicBuffer)用于记录这个Slot所涉及的缓冲区,另外还有一个BufferState变量mBufferState用于跟踪每个缓冲区的状态,比如:

        enum BufferState {

            FREE = 0, /*Buffer当前可用,也就是说可以被dequeued。此时Buffer的owner

                       可认为是BufferQueue*/

            DEQUEUED = 1, /*Buffer已经被dequeued,还未被queued或canceld。此时

                          Buffer的owner可认为是producer(应用程序),这意味着server

                          端(BufferQueue)不可以对这块缓冲区进行操作*/

            QUEUED = 2, /*Buffer已经被客户端queued,除特别情况外此时还不能对它进

                          行dequeue,而可以acquired。此时的owner是BufferQueue*/

            ACQUIRED = 3/*Buffer的owner改为consumer,可以released,

                           然后状态又返回FREE*/

        };

从上面的状态可以看出,一块Buffer大致经历的过程就是FREE->DEQUEUED->QUEUED->ACQUIRED->FREE。从owner的角度来讲,有点类似于下图的描述:

GUI系统之SurfaceFlinger(5)BufferQueue内部原理

图 11‑13 Buffer的状态迁移图

 

我们来分析下上面的buffer状态迁移简图。

从图中可以大致看出一个buffer的各个状态、引起状态迁移的条件以及各状态下的owner。参与对buffer进行管理的对象有三个:

Ø  BufferQueue

可以认为BufferQueue是一个服务中心,其它两个owner必须要通过它来管理buffer。比如说当producer想要获取一个buffer时,它不能越过BufferQueue直接与consumer进行联系,反之亦然。这有点像房产中介一样,房主与买方的任何交易都需要经过中介的同意,私自达成的协议都是违反规定的

Ø  Producer

生产者就是“填充”buffer空间的人,通常情况下当然就是应用程序。因为应用程序不断地刷新UI,从而将产生的显示数据源源不断地写到buffer中。当Producer需要使用一块buffer时,它首先会向中介BufferQueue发起dequeue申请,然后才能对指定的缓冲区进行操作。这种情况下buffer就属于producer一个人的了,它可以对buffer进行任何必要的操作,而其它owner此刻绝不能擅自插手。

当生产者认为一块buffer已经写入完成后,它进一步调用BufferQueue的queue。从字面上看这个函数是“入列”的意思,形象地表达了buffer此时的操作——把buffer归还到BufferQueue的队列中。一旦queue成功后,owner也就随之改变为BufferQueue了

Ø  Consumer

消费者是与生产者相对应的,它的操作同样受到BufferQueue的管控。当一块buffer已经就绪后,Consumer就可以开始工作了,具体的细节我们会在SurfaceFlinger中描述。这里需要特别留意的是,从各个对象所扮演的角色来看,BufferQueue是中介机构,属于服务提供方;Producer属于buffer内容的产出方,它对缓冲区的操作是一个“主动”的过程;反之,Consumer对buffer的处理则是“被动”的、“等待式”的——它必须要等到一块buffer填充完成后才能做工作。在这样的模型下,我们怎么保证Consumer可以及时的处理buffer呢?换句话说,当一块buffer数据ready后,应该怎么告知Consumer来操作呢?

仔细观察的话,可以看到BufferQueue里还同时提供了一个特别的类,名称为ConsumerListener,其中的函数接口包括:

    struct ConsumerListener :public virtual RefBase {       

        virtual voidonFrameAvailable() = 0;/*当一块buffer可以被消费时,这个函数会被调用,特别注意此

                                        时没有共享锁的保护*/

        virtual voidonBuffersReleased() = 0;/*BufferQueue通知consumer它已经释放其slot中的一个或多个

                                       GraphicBuffer引用*/

    };

 这样子就很清楚了,当有一帧数据准备就绪后,BufferQueue就会调用onFrameAvailable()来通知Consumer进行消费。