Android4.4 fence机制分析

 在任何一个系统中,无可避免的都会跟各种buffers打交道,最经典的模式就是消费-生产者模式,一个独立的buffer在它们之间的交换等操作都需要一个机制来控制每个buffer的“生命周期”,即ALLOCATION 和 RELEASE ,此外还要考虑到同步性问题,什么时候可以read buffer和write buffer都需要听从调遣。

  在android中的fence就是这样一个为了解决同步性而出现的机制。首先从fence的语义角度来分析一下它的基本原理:

Fence即栅栏,栅栏的角色与它的名字非常类似.一组线程可以使用栅栏来集体进行相互同步;在本质上,每个线程在到达某种周知的状态时调用栅栏的wait()方法,阻塞起来,以等待其它所有参与线程调用wait()方法表明它们也到达了这个状态.一旦所有的线程都到达栅栏,它们就会集体解除阻塞,并一起继续执行;引起程序调用栅栏的wait()方法进行阻塞的那个状态叫做栅栏状态。

接下来分析fence在android中的应用,这里主要涉及SurfaceFlinger中绘制buffer及显示中的相关方面。

确切的说fence在producer和consumer对buffer处理的过程中是如何协调他们同步的工作,从而保证buffer内容的准确性,而不会被篡改。

   首先我们知道一个buffer有以下几种状态:

FREE->DEQUEUED->QUEUED->ACQUIRED-FREE

  FREE状态时,producer就可以申请他了吗?答案是错的,他需要等一个signal,也就是NO_FENCE这个信号,因为有可能上一次申请的buffer正在被consumer作业中,所以要等待consumer发出finish的信号,而此时FREE状态下的buffer就好像被栅栏拦住了,这里是用Fence中wait()或者waitForever()方法,等一个NO_FENCCE信号,栅栏就会打开。进入到下一流程。

DEQUEUED是指producer已经申请了一个buffer从队列中出来了,还没有入队列或者取消buffer,这个状态下的buffer,producer想对其进行修改也就是填入UI数据时,必须等一个NO_FENCE信号,因为有可能其他owner正在对它进行操作。当信号一到,poducer就可以对其进行操作,操作完成后发出一个NO_FENCE信号。

QUEUED状态下,也就是把buffer入队列,不过在这个操作前需要等一个NO_FENCE信号,就比如上一步dequeueBuffer完成之后发的NO_FENCE.收到信号后才进行入队列操作或者取消buffer操作。这个时候它的owner就变成BufferQueue了。

ACQUIRED状态也就是producer已经对buffer填充完毕,与前面一样它也要等到一个NO_FENCE信号,然后consumer才能对其进行操作。操作完成后会释放buffer,然后发出一个NO_FENCE 信号。

 所有的fence都是在kernel层实现的,androidHAL层只是把底层的一些接口的封装及扩展。

Surfaceflinger在绘制surface过程主要是以下流程:

android AccessibilityService分屏 android fence_初始化

Surfaceflinger将计算好的layers交由给HWC,HWC根据具体情况选择对应的绘制路径。

因为openGL实现代码没有开源,所以也就不知道openGL那边对fence是如何的应用了,所以从hwcomposer入手,其实到最后发现机制是一样的,只是看不到它实现的部分罢了。

Fence到底是怎么应用的呢,它和buffer是不相关的,不能把fence看成buffer的一部分,简单说它就是一个允不允许的问题。

这里我先从大方面分析一下我对fence机制流程的理解,首先fence有好几类,它们有不同的作用,但几乎都是成对存在的。这里分析一下acquireFence 和 releaseFence,还有retire fence。

android AccessibilityService分屏 android fence_sed_02

每一个layer都有一个acquire 和release fence,每一个系列layes都有一个retirefence,注意这边的是layers!多个layer。

 acquireFence:

    禁止显示一个buffer的内容直到该fence被触发,而它是在H/W 被set up 前被发送的。

releaseFence:

     这个意味着属于这个layer的buffer已经不在被读取了,在一个buffer不在被读取的时候将会触发这个fence。

Retire fence:

     这个 scene或者 一系列的layers不再被显示到显示器上,当完成了一个frame的显示后触发这个fence。

到这里可以知道acquireFence, releaseFence是属于单个layer的,而Retire fence是属于多个layer即一个scene.那么在layer和layers对应的结构体必定有它们的影子:

在hardware/libhardware/include/hardware/hwcomposer.h中:

typedef struct hwc_layer_1 {
     ………
  int acquireFenceFd;
 
  int releaseFenceFd;
       ………
 
} hwc_layer_1_t;

可知在定义的一个layer中它们分别是两个整型变量,变量后都以Fd结尾,可想而知这将描述一个文件描述符。

同样:

typedef struct hwc_display_contents_1 {
  …………
  int retireFenceFd;
} hwc_display_contents_1_t;

 介绍完上面的各种fence之后(当然还有其他种类的fence),我用一张图来描述下fence应用的机制:

android AccessibilityService分屏 android fence_android_03

  分析到这里都是从宏观上分析fence的,大概对fence机制框架有个清楚的认识,接下来看他到底是怎么实现的。

  之前说fence实现都是在kernel层,其实观其HAL层代码,Fence::wait() and waitForever(),mege()都是对kernel层的封装。Kernel层的fence相对来说比较复杂些,毕竟是实现原理,但是究其本质fence其实就是一个文件描述符,这也响应了linux中一切皆文件的说法。

  在kernel层有三个跟fence有关的结构体:

Sync_timeline , sync_pt , sync_fence.下面简单说一下它们的作用和定义:
Sync_timeline:

  顾名思义,是个时间轴,每个流程都有自己的timeline,代表着一个自动增加的计数器。

用图形形象的来描述它如下:

android AccessibilityService分屏 android fence_sed_04

Sync_pt:

  其实就是sync point,同步点的概念,代表timeline上的一个特别的值。它有三种状态:active signalederror。

 

android AccessibilityService分屏 android fence_android_05

Sync_fence

  它是一系列sync_pt的集合,实际上是个文件描述符可以被传到用户空间,也就是这一个特性,让hal层fence和kernel扯上联系。

  

android AccessibilityService分屏 android fence_初始化_06

 上面就是这三个结构体的基本介绍,还有跟fence相关的API这里就不详细介绍,后面分析LCD时在细究。

 

  一开始就给出SF合成图像到显示的两个流程,这里重点分析hwc这条路径:

因为android一旦启动后,绘制图像就是一个循环的状态,所以为了方便研究,从android系统开机动画开始:

     第一步就是客户端请求一个buffer(这里暂不说成app),因为是刚开始所以一切的fence都属于初始化状态或者还没被创造,(从理论上来讲这个时候一切都是空闲的,无论是buffer还是其他什么的,所以我按照这种假设模式继续下去分析,事实是怎样有待考究)因此第一次dequeue一个Buffer的时候就不需要等待display来触发fence了,也不会担心SF是不是在对这个buffer进行计算合成,就这样一步步走向SF计算合成前,开始准备分派hwc渲染的时候,第一次对acquireFenceFd 和 releaseFenceFd还有retireFenceFd进行初始化,在setUpHWComposer中的createWorkList完成的:

 关键代结构码如下

其中hwc_layer_1 framebufferTarget;  

hwc_display_contents_1 list;
For(;dpy<mDisplays.size();)
{
  For(;numLayers;)
     disp.framebufferTarget->acquireFenceFd =-1;
     disp.framebufferTarget->releaseFenceFd= -1;
}
disp.list->retireFenceFd = -1;
}

 这样的初始化印证了之前所说的acq,rel分别对应每个layer,而retire对应的是layers。

 Set up之后,开始进行计算合成。最后走到postFramebuffer中的HWComposer::commit()---》set(…)---》hwc_set()

 在hwc_set中完成了渲染工作,然后通过ioctl交给了fb去显示,这里贴出hwc_set中:

一直运行到hwc_sync 会堵塞在这个函数中的wait里:

voidhwc_sync(hwc_display_contents_1_t  *list)
{
 for (int i=0; i<list->numHwLayers; i++)
 {
     if(list->hwLayers[i].acquireFenceFd>0)
     {
 sync_wait(list->hwLayers[i].acquireFenceFd,500);       ALOGV("fenceFd=%d,name=%s",list->hwLayers[i].acquireFenceFd,list->hwLayers[i].LayerName);
     }
 }
}

由上面的红色代码行可知他在等acquireFence这个信号。 

if (layer->acquireFenceFd>0)
{
    g_sync.acq_fence_fd[k] =layer->acquireFenceFd;
}
ioctl(context->fbFd,RK_FBIOSET_CONFIG_DONE, &g_sync);
list->hwLayers[0].releaseFenceFd= g_sync.rel_fence_fd[0];
list->hwLayers[1].releaseFenceFd= g_sync.rel_fence_fd[1];
//list->retireFenceFd =g_sync.ret_fence_fd;
close(g_sync.ret_fence_fd);    
list->retireFenceFd = -1;

rel_fence_fd,看名字就能猜出这是存放对应两个fence的fd,第一步是把之前初始化的每个layer的acqFenfd保存到数组中,接着display就开始显示了,ioctl映射到内核中fd驱动程序的ioctl。

接下来分析fb驱动中跟fence相关的代码: 

首先定义了跟fence相关的一些变量:

struct sync_fence *release_fence;
         structsync_fence *retire_fence;
 
         structsync_pt *release_sync_pt;
         structsync_pt *retire_sync_pt;
 
         structsync_fence *layer2_fence;
         structsync_pt *layer2_pt;

 其中fence有三类 releaseretire 和layer2 。

接着寻找没有被用过的fd保存到rel_fence_fd中: 

dev_drv->win_data.rel_fence_fd[0]=  get_unused_fd();
dev_drv->win_data.rel_fence_fd[1]=  get_unused_fd();

然后开始创建fence:

release_sync_pt= sw_sync_pt_create(dev_drv->timeline, dev_drv->timeline_max);
                                      release_fence= sync_fence_create("rel_fence", release_sync_pt);
                                      sync_fence_install(release_fence,dev_drv->win_data.rel_fence_fd[0]);
 
                                      layer2_pt= sw_sync_pt_create(dev_drv->timeline, dev_drv->timeline_max);
                                      layer2_fence=sync_fence_create("rel2_fence", layer2_pt);
                                      sync_fence_install(layer2_fence,dev_drv->win_data.rel_fence_fd[1]);
 
                                      retire_sync_pt= sw_sync_pt_create(dev_drv->timeline, dev_drv->timeline_max);
                                      retire_fence= sync_fence_create("ret_fence", retire_sync_pt);
                                      sync_fence_install(retire_fence,dev_drv->win_data.ret_fence_fd);

 创建过程这里省略掉,fence在这里被创建完之后就阻塞触发了(等待一个条件:当buffer被显示后马上触发),触发的函数在sync_fence_create中的sync_fence_signal_pt(pt);在这里是一整个过程fence第一次被触发。

触发的是releaseFence 和retiredfence,接着往下走:

程序下一步会运行:

if (dev_drv->wait_fs == 1) { //wait for new frame start in kernel
rk_fb_update_reg(dev_drv,regs);
                                               kfree(regs);
                                               mutex_unlock(&dev_drv->update_regs_list_lock);
                                      }

接着看rk_fb_update_reg(dev_drv,regs)中的关键代码:

        

sw_sync_timeline_inc(dev_drv->timeline,1);
         if(dev_drv->win_data.acq_fence_fd[0]>= 0)
         {
                   for(i=0;i<RK30_MAX_LAYER_SUPPORT;i++){
                            if(dev_drv->win_data.acq_fence_fd[i]> 0){
                                     put_unused_fd(dev_drv->win_data.acq_fence_fd[i]);
                                     printk("acq_fd=%d\n",dev_drv->win_data.acq_fence_fd[i]);
                            }        
                            rk_fb_free_dma_buf(®s->dma_buf_data[i]);
                   }
         }

核心功能大概就是让之前保存在acq_fence_fd数组中的fd无效,看似简单的一个操作,好像对acqFenceFd只是单纯的赋值为-1,但是从源代码中定义acqFenceFd的说明:

/*Sync fence object that will be signaled when the buffer's
            * contents are available. May be -1 if the contents are already
            * available.*/

上面是源代码中的解释,由此可以看出当fd为-1时acqFenceFd会被触发。 

当程序运行到这里的时候,由于只是当中的一个线程,所以前面客户端请求buffer的操作早已经开始了,而且已经在等待相关的fence了。触发了releasefence之后用户那边收到之后就开始dequeue一个buffer进行填充surface了。

 

用一张图来表示下这个过程:

android AccessibilityService分屏 android fence_surfaceflinger_07