??? ? ? 7. 圖形緩沖區(qū)的注銷過程
?? ? ? 圖形緩沖區(qū)使用完成之后,就需要從當前進程中注銷。前面提到,注銷圖形緩沖區(qū)是由Gralloc模塊中的函數(shù)gralloc_unregister_buffer來實現(xiàn)的,這個函數(shù)實現(xiàn)在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: int?gralloc_unregister_buffer(gralloc_module_t?const*?module,??????????buffer_handle_t?handle)??{??????if?(private_handle_t::validate(handle)?<?0)??????????return?-EINVAL;??????????????private_handle_t*?hnd?=?(private_handle_t*)handle;??????if?(hnd->pid?!=?getpid())?{??????????if?(hnd->base)?{??????????????gralloc_unmap(module,?handle);??????????}??????}??????return?0;??}?? ?? ? ? ?這個函數(shù)同樣是首先調(diào)用private_handle_t類的靜態(tài)成員函數(shù)validate來驗證參數(shù)handle指向的一塊圖形緩沖區(qū)的確是由Gralloc模塊分配的,接著再將將參數(shù)handle指向的一塊圖形緩沖區(qū)轉(zhuǎn)換為一個private_handle_t結(jié)構(gòu)體hnd來訪問。 ?? ? ? ?一塊圖形緩沖區(qū)只有被注冊過,即被Gralloc模塊中的函數(shù)gralloc_register_buffer注冊過,才需要注銷,而由函數(shù)gralloc_register_buffer注冊的圖形緩沖區(qū)都不是由當前進程分配的,因此,當前進程在注銷一個圖形緩沖區(qū)的時候,會檢查要注銷的圖形緩沖區(qū)是否是由自己分配的。如果是由自己分配的話,那么它什么也不做就返回了。 ?? ? ? ?假設(shè)要注銷的圖形緩沖區(qū)hnd不是由當前進程分配的,那么接下來就會調(diào)用另外一個函數(shù)galloc_unmap來注銷圖形緩沖區(qū)hnd。 ?? ? ? ?函數(shù)galloc_unmap也是實現(xiàn)在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: static?int?gralloc_unmap(gralloc_module_t?const*?module,??????????buffer_handle_t?handle)??{??????private_handle_t*?hnd?=?(private_handle_t*)handle;??????if?(!(hnd->flags?&?private_handle_t::PRIV_FLAGS_FRAMEBUFFER))?{??????????void*?base?=?(void*)hnd->base;??????????size_t?size?=?hnd->size;????????????????????if?(munmap(base,?size)?<?0)?{??????????????LOGE("Could?not?unmap?%s",?strerror(errno));??????????}??????}??????hnd->base?=?0;??????return?0;??}?? ?? ? ? ?這個函數(shù)的實現(xiàn)與前面所分析的函數(shù)gralloc_map的實現(xiàn)是類似的,只不過它執(zhí)行的是相反的操作,即將解除一個指定的圖形緩沖區(qū)在當前進程的地址空間中的映射,從而完成對這個圖形緩沖區(qū)的注銷工作。 ?? ? ? ?這樣,圖形緩沖區(qū)的注銷過程就分析完成了,接下來我們再繼續(xù)分析一個圖形緩沖區(qū)是如何被渲染到系統(tǒng)幀緩沖區(qū)去的,即它的內(nèi)容是如何繪制在設(shè)備顯示屏中的。 ?? ? ? ?8. 圖形緩沖區(qū)的渲染過程 ?? ? ? ?用戶空間的應(yīng)用程序?qū)嬅鎯?nèi)容寫入到圖形緩沖區(qū)中去之后,還需要將圖形緩沖區(qū)渲染到系統(tǒng)幀緩沖區(qū)中去,這樣才可以把畫面繪制到設(shè)備顯示屏中去。前面提到,渲染圖形緩沖區(qū)是由Gralloc模塊中的函數(shù)fb_post來實現(xiàn)的,這個函數(shù)實現(xiàn)在文件hardware/libhardware/modules/gralloc/framebuffer.cpp中,如下所示: static?int?fb_post(struct?framebuffer_device_t*?dev,?buffer_handle_t?buffer)??{??????if?(private_handle_t::validate(buffer)?<?0)??????????return?-EINVAL;????????fb_context_t*?ctx?=?(fb_context_t*)dev;????????private_handle_t?const*?hnd?=?reinterpret_cast<private_handle_t?const*>(buffer);??????private_module_t*?m?=?reinterpret_cast<private_module_t*>(??????????????dev->common.module);????????if?(hnd->flags?&?private_handle_t::PRIV_FLAGS_FRAMEBUFFER)?{??????????const?size_t?offset?=?hnd->base?-?m->framebuffer->base;??????????m->info.activate?=?FB_ACTIVATE_VBL;??????????m->info.yoffset?=?offset?/?m->finfo.line_length;??????????if?(ioctl(m->framebuffer->fd,?FBIOPUT_VSCREENINFO,?&m->info)?==?-1)?{??????????????LOGE("FBIOPUT_VSCREENINFO?failed");??????????????m->base.unlock(&m->base,?buffer);??????????????return?-errno;??????????}??????????m->currentBuffer?=?buffer;????????}?else?{????????????????????????????????void*?fb_vaddr;??????????void*?buffer_vaddr;????????????m->base.lock(&m->base,?m->framebuffer,??????????????????GRALLOC_USAGE_SW_WRITE_RARELY,??????????????????0,?0,?m->info.xres,?m->info.yres,??????????????????&fb_vaddr);????????????m->base.lock(&m->base,?buffer,??????????????????GRALLOC_USAGE_SW_READ_RARELY,??????????????????0,?0,?m->info.xres,?m->info.yres,??????????????????&buffer_vaddr);????????????memcpy(fb_vaddr,?buffer_vaddr,?m->finfo.line_length?*?m->info.yres);????????????m->base.unlock(&m->base,?buffer);??????????m->base.unlock(&m->base,?m->framebuffer);??????}????????return?0;??}?? ?? ? ? ?參數(shù)buffer用來描述要渲染的圖形緩沖區(qū),它指向的必須要是一個private_handle_t結(jié)構(gòu)體,這是通過調(diào)用private_handle_t類的靜態(tài)成員函數(shù)validate來驗證的。驗證通過之后,就可以將參數(shù)buffer所描述的一個buffer_handle_t結(jié)構(gòu)體轉(zhuǎn)換成一個private_handle_t結(jié)構(gòu)體hnd。 ?? ? ? ?參數(shù)dev用來描述在Gralloc模塊中的一個fb設(shè)備。從前面第3部分的內(nèi)容可以知道,在打開fb設(shè)備的時候,Gralloc模塊返回給調(diào)用者的實際上是一個fb_context_t結(jié)構(gòu)體,因此,這里就可以將參數(shù)dev所描述的一個framebuffer_device_t結(jié)構(gòu)體轉(zhuǎn)換成一個fb_context_t結(jié)構(gòu)體ctx。 ?? ? ? ?參數(shù)dev的成員變量common指向了一個hw_device_t結(jié)構(gòu)體,這個結(jié)構(gòu)體的成員變量module指向了一個Gralloc模塊。從前面第1部分的內(nèi)容可以知道,一個Gralloc模塊是使用一個private_module_t結(jié)構(gòu)體來描述的,因此,我們可以將dev->common.moudle轉(zhuǎn)換成一個private_module_t結(jié)構(gòu)體m。 ?? ? ? ?由于private_handle_t結(jié)構(gòu)體hnd所描述的圖形緩沖區(qū)可能是在系統(tǒng)幀緩沖區(qū)分配的,也有可能是內(nèi)存中分配的,因此,我們分兩種情況來討論圖形緩沖區(qū)渲染過程。 ?? ? ? ?當private_handle_t結(jié)構(gòu)體hnd所描述的圖形緩沖區(qū)是在系統(tǒng)幀緩沖區(qū)中分配的時候,即這個圖形緩沖區(qū)的標志值flags的PRIV_FLAGS_FRAMEBUFFER位等于1的時候,我們是不需要將圖形緩沖區(qū)的內(nèi)容拷貝到系統(tǒng)幀緩沖區(qū)去的,因為我們將內(nèi)容寫入到圖形緩沖區(qū)的時候,已經(jīng)相當于是將內(nèi)容寫入到了系統(tǒng)幀緩沖區(qū)中去了。雖然在這種情況下,我們不需要將圖形緩沖區(qū)的內(nèi)容拷貝到系統(tǒng)幀緩沖區(qū)去,但是我們需要告訴系統(tǒng)幀緩沖區(qū)設(shè)備將要渲染的圖形緩沖區(qū)作為系統(tǒng)當前的輸出圖形緩沖區(qū),這樣才可以將要渲染的圖形緩沖區(qū)的內(nèi)容繪制到設(shè)備顯示屏來。例如,假設(shè)系統(tǒng)幀緩沖區(qū)有2個圖形緩沖區(qū),當前是以第1個圖形緩沖區(qū)作為輸出圖形緩沖區(qū)的,這時候如果我們需要渲染第2個圖形緩沖區(qū),那么就必須告訴系統(tǒng)幀繪沖區(qū)設(shè)備,將第2個圖形緩沖區(qū)作為輸出圖形緩沖區(qū)。 ?? ? ? 設(shè)置系統(tǒng)幀緩沖區(qū)的當前輸出圖形緩沖區(qū)是通過IO控制命令FBIOPUT_VSCREENINFO來進行的。IO控制命令FBIOPUT_VSCREENINFO需要一個fb_var_screeninfo結(jié)構(gòu)體作為參數(shù)。從前面第3部分的內(nèi)容可以知道,private_module_t結(jié)構(gòu)體m的成員變量info正好保存在我們所需要的這個fb_var_screeninfo結(jié)構(gòu)體。有了個m->info這個fb_var_screeninfo結(jié)構(gòu)體之后,我們只需要設(shè)置好它的成員變量yoffset的值(不用設(shè)置成員變量xoffset的值是因為所有的圖形緩沖區(qū)的寬度是相等的),就可以將要渲染的圖形緩沖區(qū)設(shè)置為系統(tǒng)幀緩沖區(qū)的當前輸出圖形緩沖區(qū)。fb_var_screeninfo結(jié)構(gòu)體的成員變量yoffset保存的是當前輸出圖形緩沖區(qū)在整個系統(tǒng)幀緩沖區(qū)的縱向偏移量,即Y偏移量。我們只需要將要渲染的圖形緩沖區(qū)的開始地址hnd->base的值減去系統(tǒng)幀緩沖區(qū)的基地址m->framebuffer->base的值,再除以圖形緩沖區(qū)一行所占據(jù)的字節(jié)數(shù)m->finfo.line_length,就可以得到所需要的Y偏移量。 ?? ? ? ?在執(zhí)行IO控制命令FBIOPUT_VSCREENINFO之前,還會將作為參數(shù)的fb_var_screeninfo結(jié)構(gòu)體的成員變量activate的值設(shè)置FB_ACTIVATE_VBL,表示要等到下一個垂直同步事件出現(xiàn)時,再將當前要渲染的圖形緩沖區(qū)的內(nèi)容繪制出來。這樣做的目的是避免出現(xiàn)屏幕閃爍,即避免前后兩個圖形緩沖區(qū)的內(nèi)容各有一部分同時出現(xiàn)屏幕中。 ?? ? ? ?成功地執(zhí)行完成IO控制命令FBIOPUT_VSCREENINFO之后,函數(shù)還會將當前被渲染的圖形緩沖區(qū)保存在private_module_t結(jié)構(gòu)體m的成員變量currentBuffer中,以便可以記錄當前被渲染的圖形緩沖區(qū)是哪一個。 ?? ? ? ?當private_handle_t結(jié)構(gòu)體hnd所描述的圖形緩沖區(qū)是在內(nèi)存中分配的時候,即這個圖形緩沖區(qū)的標志值flags的PRIV_FLAGS_FRAMEBUFFER位等于0的時候,我們就需要將它的內(nèi)容拷貝到系統(tǒng)幀緩沖區(qū)中去了。這個拷貝的工作是通過調(diào)用函數(shù)memcpy來完成的。在拷貝之前,我們需要三個參數(shù)。第一個參數(shù)是要渲染的圖形緩沖區(qū)的起址地址,這個地址保存在參數(shù)buffer所指向的一個private_handle_t結(jié)構(gòu)體中。第二個參數(shù)是要系統(tǒng)幀緩沖區(qū)的基地址,這個地址保存在private_module_t結(jié)構(gòu)體m的成員變量framebuffer所指向的一個private_handle_t結(jié)構(gòu)體中。第三個參數(shù)是要拷貝的內(nèi)容的大小,這個大小就剛好是一個屏幕像素所占據(jù)的內(nèi)存的大小。屏幕高度由m->info.yres來描述,而一行屏幕像素所占用的字節(jié)數(shù)由m->finfo.line_length來描述,將這兩者相乘,就可以得到一個屏幕像素所占據(jù)的內(nèi)存的大小。 ?? ? ? ?在將一塊內(nèi)存緩沖區(qū)的內(nèi)容拷貝到系統(tǒng)幀緩沖區(qū)中去之前,需要對這兩塊緩沖區(qū)進行鎖定,以保證在拷貝的過程中,這兩塊緩沖區(qū)的內(nèi)容不會被修改。這個鎖定的工作是由Gralloc模塊中的函數(shù)gralloc_lock來實現(xiàn)的。從前面第1部分的內(nèi)容可以知道,Gralloc模塊中的函數(shù)gralloc_lock的地址正好就保存在private_module_t結(jié)構(gòu)體m的成員變量base所描述的一個gralloc_module_t結(jié)構(gòu)體的成員函數(shù)lock中。 ?? ? ? ?在調(diào)用函數(shù)gralloc_lock來鎖定一塊緩沖區(qū)之后,還可以通過最后一個輸出參數(shù)來獲得被鎖定的緩沖區(qū)的開始地址,因此,通過調(diào)用函數(shù)gralloc_lock來鎖定要渲染的圖形緩沖區(qū)以及系統(tǒng)幀緩沖區(qū),就可以得到前面所需要的第一個和第二個參數(shù)。 ?? ? ? ?將要渲染的圖形緩沖區(qū)的內(nèi)容拷貝到系統(tǒng)幀緩沖區(qū)之后,就可以解除前面對它們的鎖定了,這個解鎖的工作是由Gralloc模塊中的函數(shù)gralloc_unlock來實現(xiàn)的。從前面第1部分的內(nèi)容可以知道,Gralloc模塊中的函數(shù)gralloc_unlock的地址正好就保存在private_module_t結(jié)構(gòu)體m的成員變量base所描述的一個gralloc_module_t結(jié)構(gòu)體的成員函數(shù)unlock中。 ?? ? ? ?這樣,一個圖形緩沖區(qū)的渲染過程就分析完成了。
總結(jié)
以上是生活随笔為你收集整理的Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(10)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。