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

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

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

每一个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();)

{

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;

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

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了。

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

时间: 2024-10-14 00:54:39

Android4.4 fence机制分析的相关文章

Android4.0 Surface机制分析

1. java层面的Surface 对于Surface我们的认识主要是android的类Surface, android的文档描述Surface是"Handle onto a raw buffer that is being managed by the screen compositor",这个描述透漏出两个信息:首先,Surface是一个raw buffer的句柄,通过它去管理一个raw buffer,其次,Surface本身是由screen compositor来管理的.但是ra

转自Android内存机制分析1——了解Android堆和栈

转自http://www.cnblogs.com/mythou/p/3202238.html 昨天用Gallery做了一个图片浏览选择开机画面的功能,当我加载的图片多了就出现OOM问题.以前也出现过这个问题,那时候并没有深究.这次打算好好分析一下Android的内存机制. 因为我以前是做VC++开发,因此对C++在Window下的内存机制还是比较了解.不过转到Android后,一直都没有刻意去处理内存问题,因为脑子里一直想着Java的GC机制.不过现在想想,自己对Android的GC和内存管理并

android4.3 按键消息处理分析

Android4.3按键消息处理与之前的版本有稍微的区别,基本原理还是一样的,这里主要从两个阶段来分析: 1.前期的准备工作,即开机时启动相应的的线程,静候按键事件的来临 2.当有按键消息时,进行消息的分发等处理 先看一张类图: 从类图中看出,主要涉及到的类有PhoneWindowManager.WindowManagerService.inputManagerService. InputManager 先看第一个问题,前期的准备工作: 1.开机时先启动inputManagerService,由

QT开发(六十三)——QT事件机制分析

QT开发(六十三)--QT事件机制分析 一.事件机制 事件是由系统或者QT平台本身在不同的时刻发出的.当用户按下鼠标.敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件.一些事件在对用户操作做出响应时发出,如键盘事件等:另一些事件则是由系统自动发出,如计时器事件. 事件的出现,使得程序代码不会按照原始的线性顺序执行.线性顺序的程序设计风格不适合处理复杂的用户交互,如用户交互过程中,用户点击"打开文件"将开始执行打开文件的操作,用户点击"保存文件"将开始执

Linux通信之poll机制分析

poll机制分析 韦东山 2009.12.10 所有的系统调用,基于都可以在它的名字前加上"sys_"前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open.sys_read.sys_write.sys_poll. 一.内核框架: 对于系统调用poll或select,它们对应的内核函数都是sys_poll.分析sys_poll,即可理解poll机制. sys_poll函数位于fs/select.c文件中,代码如下:

Nginx处理stale事件机制分析

Nginx为提高效率采用描述符缓冲池(连接池)来处理tcp连接,一个连接对应一个读事件和一个写事件,nginx在启动的时候会创建好所用连接和事件,当事件来的时候不用再创建,然而连接池的使用却存在stale事件的问题,以下将详细分析Nginx是如何处理stale事件的,该问题涉及到epoll.Nginx连接与事件的相关知识. 1      Epoll的实现原理 epoll相关的系统调用有:epoll_create, epoll_ctl和epoll_wait.Linux-2.6.19又引入了可以屏蔽

Linux x86_64 APIC中断路由机制分析

不同CPU体系间的中断控制器工作原理有较大差异,本文是<Linux mips64r2 PCI中断路由机制分析>的姊妹篇,主要分析Broadwell-DE X86_64 APIC中断路由原理.中断配置和处理过程,并尝试回答如下问题: 为什么x86中断路由使用IO-APIC/LAPIC框架,其有什么价值? pin/irq/vector的区别.作用,取值范围和分配机制? x86_64 APIC关键概念 Pin 此处的pin特指APIC的中断输入引脚,与内外部设备的中断输入信号相连.从上图中可以看出,

[转]易语言消息机制分析(消息拦截原理)

标 题: [原创]易语言消息机制分析(消息拦截原理)作 者: 红绡枫叶时 间: 2014-12-17,12:41:44链 接: http://bbs.pediy.com/showthread.php?t=195626 我自己做了个易语言的sig签名,方便分析的时候用.易语言例子是静态编译的.版本 5.11易语言其实是基于mfc的,它依然需要mfc的消息派发机制,只不过,自己当了系统与用户间的代理人.所有的消息都要经它转发而已.我在MFC的消息派发函数_AfxDispatchCmdMsg下断点,总

UVM基础之---------uvm report 机制分析

uvm 中的信息报告机制相对来说比较简单,功能上来说主要分为两部分: 第一通过ID对component的信息报告冗余级别进行控制,针对每个冗余级别进行不同的行为控制.这部分工作主要由uvm_report_hander来实现: 主要涉及到的方法有get_report_verbosity_level(severity, id)/get_report_action(severity,id) == uvm_action'(UVM_NO_ACTION) 第二是对message进行格式化的输出,这部分工作主