Android4.4 fence机制分析

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过程主要是以下流程:

\

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

因为openGL实现代码没有开源,所以也就不知道Z喎�"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcGVuR0zEx7HfttRmZW5jZcrHyOe6zrXE06bTw8HLo6zL+dLUtNNod2NvbXBvc2VyyOvK1qOsxuTKtbW91+6687eiz9a7+tbGysfSu9H5tcSjrNa7yse/tLK7tb3L/Mq1z9a1xLK/t9aw1cHLoaM8L3A+CjxwPkZlbmNltb2118rH1PXDtNOm08O1xMTYo6zL/LrNYnVmZmVyyseyu8/gudi1xKOssrvE3LDRZmVuY2W/tLPJYnVmZmVytcTSu7K/t9ajrLzytaXLtcv8vs3Kx9K7uPbUyrK71MrQ7bXEzsrM4qGjPC9wPgo8cD7V4sDvztLPyLTTtPO3vcPmt9bO9tK7z8LO0rbUZmVuY2W7+tbGwfezzLXEwO294qOsytfPyGZlbmNl09C6w7y4wOCjrMv8w8fT0LK7zay1xNf308OjrLWrvLi69ba8ysezybbUtObU2rXEoaPV4sDvt9bO9tK7z8JhY3F1aXJlRmVuY2Ugus0gcmVsZWFzZUZlbmNlo6y7udPQcmV0aXJlIGZlbmNloaM8L3A+CjxwPjxpbWcgc3JjPQ=="http://www.2cto.com/uploadfile/Collfiles/20140913/2014091308422543.png" alt="\">

每一个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应用的机制:

\

分析到这里都是从宏观上分析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,代表着一个自动增加的计数器。

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

\

Sync_pt:

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

\

Sync_fence

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

\

上面就是这三个结构体的基本介绍,还有跟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();)< p="">

{

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; inumHwLayers; 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;

首先这里有两个数组 acq_fence_fd和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++){< p="">

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(&regs->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了。

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值