Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android4.4 fence機制分析

Android4.4 fence機制分析

編輯:關於Android編程

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喎?/kf/ware/vc/" target="_blank" class="keylink">vcGVuR0zEx7HfttRmZW5jZcrHyOe6zrXE06bTw8HLo6zL+dLUtNNod2NvbXBvc2VyyOvK1qOsxuTKtbW91+6687eiz9a7+tbGysfSu9H5tcSjrNa7yse/tLK7tb3L/Mq1z9a1xLK/t9aw1cHLoaM8L3A+CjxwPkZlbmNltb2118rH1PXDtNOm08O1xMTYo6zL/LrNYnVmZmVyyseyu8/gudi1xKOssrvE3LDRZmVuY2W/tLPJYnVmZmVytcTSu7K/t9ajrLzytaXLtcv8vs3Kx9K7uPbUyrK71MrQ7bXEzsrM4qGjPC9wPgo8cD7V4sDvztLPyLTTtPO3vcPmt9bO9tK7z8LO0rbUZmVuY2W7+tbGwfezzLXEwO294qOsytfPyGZlbmNl09C6w7y4wOCjrMv8w8fT0LK7zay1xNf308OjrLWrvLi69ba8ysezybbUtObU2rXEoaPV4sDvt9bO9tK7z8JhY3F1aXJlRmVuY2Ugus0gcmVsZWFzZUZlbmNlo6y7udPQcmV0aXJlIGZlbmNloaM8L3A+CjxwPjxpbWcgc3JjPQ=="/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

{

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

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

用一張圖來表示下這個過程:

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved