前面分析dequeueBufferqueueBuffer时,看到Fence都是跳过的,只知道这是一种资源同步机制,具体不了解,这两天在网上查阅了相关资料,对Fence机制有了一个大致的了解,本篇总结一下。

什么是同步?java中有个synchronized关键字,被synchronized修饰的方法同一时间只允许一个线程访问,其实Fence机制也有点类似synchronized的,它的主要作用也是限制生产者消费者对同一块buffer的访问,我们想想如果没有Fence机制,那么生产者消费者模型应该这样:

生产者生产图形数据,数据完全生产完之后将buffer转移到BufferQueue,消费者从BufferQueue获取buffer,将buffer给到SurfaceFlinger合成,完全使用完毕之后将buffer再放入BufferQueue,生产者又开始申请buffer…如此循环(这种模型会将buffer的拥有权/使用权一并转移,意味着谁拥有谁才可以使用)。

这种模型对于只依靠CPU工作的绘制过程是可用的,例如上层的2Dcanvas绘图,即使用ViewonDraw方法里面的drawXXX,使用这种绘制方法Fence的fd就为-1,代表不需要Fence,所以纯CPU绘制是不需要同步机制的,反正都是它自己一个人工作,什么时候生产完,什么时候消费完都是知道的,但目前大多数的绘制方法都是采用3Dopengl绘图,使用的GPU,而CPU和GPU的工作是异步的,CPU甚至都无法知道GPU何时工作完成,这种情况如果等生产者完全生产完图形数据或者消费者完全消费完buffer(完成生产完意味这CPU,GPU都完成各自的工作)再将buffer转移至BufferQueue效率会非常低,而且如果GPU绘制耗时会使CPU一直等待GPU的完成,此时CPU也不能做其他事情了。还有一点如果按上面模型,buffer在转移过程中是不会有人使用的,理想情况应该如下:

android分片加载 android fence_数据


参考博客:Android中的GraphicBuffer同步机制-Fence

上图情况表明了buffer一直在被使用,完成生产之后里面消费,消费完之后立马生产,所以引入了Fence机制,Fence机制核心其实是等待/唤醒。

每一个buffer都一个Fence状态,代表这块buffer是否还在被上一个使用者使用完,并且在转移时都会携带当前Fence的fd,然后可以调用Fence的wait或者waitForever查询Fence状态,如果还有人在使用则等待,否则就可以使用。Fence按作用大体分两种:acquireFencereleaseFence。前者用于生产者通知消费者生产已完成,后者用于消费者通知生产者消费已完成。

例如:
我们通过dequeueBuffer函数申请一块buffer之后,使用opengl绘制,当调用一系列gl命令,CPU完成了命令执行返回之后将此buffer转移到BufferQueue,并通过回调函数onFrameAvailable通知消费者,但此时消费者并不能真正拿buffer去合成,因为有可能GPU还在使用buffer,需要等待一个acquireFence,由GPU发出,只有acquireFence才能代表buffer完全生产完。

反过来,当消费者拿到buffer进行合成之后会经过一系列调用将buffer转移回BufferQueue,此时如果通过dequeueBuffer函数申请的刚好是这块刚刚消费完的buffer,那么同样需要等待releaseFence表明这快buffer完全使用完毕。

Fence机制有效的处理了CPU和GPU的同步问题,通过分离buffer的拥有权和使用权提高了buffer处理效率。