Android幀緩衝區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析(10)

        7. 圖形緩衝區的註銷過程

       圖形緩衝區使用完成之後,就需要從當前進程中註銷。前面提到,註銷圖形緩衝區是由Gralloc模塊中的函數gralloc_unregister_buffer來實現的,這個函數實現在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
  1. int gralloc_unregister_buffer(gralloc_module_t const* module,  
  2.         buffer_handle_t handle)  
  3. {  
  4.     if (private_handle_t::validate(handle) < 0)  
  5.         return -EINVAL;  
  6.   
  7.     // never unmap buffers that were created in this process  
  8.     private_handle_t* hnd = (private_handle_t*)handle;  
  9.     if (hnd->pid != getpid()) {  
  10.         if (hnd->base) {  
  11.             gralloc_unmap(module, handle);  
  12.         }  
  13.     }  
  14.     return 0;  
  15. }  
        這個函數同樣是首先調用private_handle_t類的靜態成員函數validate來驗證參數handle指向的一塊圖形緩衝區的確是由Gralloc模塊分配的,接着再將將參數handle指向的一塊圖形緩衝區轉換爲一個private_handle_t結構體hnd來訪問。
 
        一塊圖形緩衝區只有被註冊過,即被Gralloc模塊中的函數gralloc_register_buffer註冊過,才需要註銷,而由函數gralloc_register_buffer註冊的圖形緩衝區都不是由當前進程分配的,因此,當前進程在註銷一個圖形緩衝區的時候,會檢查要註銷的圖形緩衝區是否是由自己分配的。如果是由自己分配的話,那麼它什麼也不做就返回了。
        假設要註銷的圖形緩衝區hnd不是由當前進程分配的,那麼接下來就會調用另外一個函數galloc_unmap來註銷圖形緩衝區hnd。
        函數galloc_unmap也是實現在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
  1. static int gralloc_unmap(gralloc_module_t const* module,  
  2.         buffer_handle_t handle)  
  3. {  
  4.     private_handle_t* hnd = (private_handle_t*)handle;  
  5.     if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {  
  6.         void* base = (void*)hnd->base;  
  7.         size_t size = hnd->size;  
  8.         //LOGD("unmapping from %p, size=%d", base, size);  
  9.         if (munmap(base, size) < 0) {  
  10.             LOGE("Could not unmap %s", strerror(errno));  
  11.         }  
  12.     }  
  13.     hnd->base = 0;  
  14.     return 0;  
  15. }  
        這個函數的實現與前面所分析的函數gralloc_map的實現是類似的,只不過它執行的是相反的操作,即將解除一個指定的圖形緩衝區在當前進程的地址空間中的映射,從而完成對這個圖形緩衝區的註銷工作。
 
        這樣,圖形緩衝區的註銷過程就分析完成了,接下來我們再繼續分析一個圖形緩衝區是如何被渲染到系統幀緩衝區去的,即它的內容是如何繪製在設備顯示屏中的。
        8. 圖形緩衝區的渲染過程
        用戶空間的應用程序將畫面內容寫入到圖形緩衝區中去之後,還需要將圖形緩衝區渲染到系統幀緩衝區中去,這樣纔可以把畫面繪製到設備顯示屏中去。前面提到,渲染圖形緩衝區是由Gralloc模塊中的函數fb_post來實現的,這個函數實現在文件hardware/libhardware/modules/gralloc/framebuffer.cpp中,如下所示:
  1. static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)  
  2. {  
  3.     if (private_handle_t::validate(buffer) < 0)  
  4.         return -EINVAL;  
  5.   
  6.     fb_context_t* ctx = (fb_context_t*)dev;  
  7.   
  8.     private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);  
  9.     private_module_t* m = reinterpret_cast<private_module_t*>(  
  10.             dev->common.module);  
  11.   
  12.     if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {  
  13.         const size_t offset = hnd->base - m->framebuffer->base;  
  14.         m->info.activate = FB_ACTIVATE_VBL;  
  15.         m->info.yoffset = offset / m->finfo.line_length;  
  16.         if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) {  
  17.             LOGE("FBIOPUT_VSCREENINFO failed");  
  18.             m->base.unlock(&m->base, buffer);  
  19.             return -errno;  
  20.         }  
  21.         m->currentBuffer = buffer;  
  22.   
  23.     } else {  
  24.         // If we can't do the page_flip, just copy the buffer to the front   
  25.         // FIXME: use copybit HAL instead of memcpy  
  26.   
  27.         void* fb_vaddr;  
  28.         void* buffer_vaddr;  
  29.   
  30.         m->base.lock(&m->base, m->framebuffer,  
  31.                 GRALLOC_USAGE_SW_WRITE_RARELY,  
  32.                 0, 0, m->info.xres, m->info.yres,  
  33.                 &fb_vaddr);  
  34.   
  35.         m->base.lock(&m->base, buffer,  
  36.                 GRALLOC_USAGE_SW_READ_RARELY,  
  37.                 0, 0, m->info.xres, m->info.yres,  
  38.                 &buffer_vaddr);  
  39.   
  40.         memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);  
  41.   
  42.         m->base.unlock(&m->base, buffer);  
  43.         m->base.unlock(&m->base, m->framebuffer);  
  44.     }  
  45.   
  46.     return 0;  
  47. }  
        參數buffer用來描述要渲染的圖形緩衝區,它指向的必須要是一個private_handle_t結構體,這是通過調用private_handle_t類的靜態成員函數validate來驗證的。驗證通過之後,就可以將參數buffer所描述的一個buffer_handle_t結構體轉換成一個private_handle_t結構體hnd。
        參數dev用來描述在Gralloc模塊中的一個fb設備。從前面第3部分的內容可以知道,在打開fb設備的時候,Gralloc模塊返回給調用者的實際上是一個fb_context_t結構體,因此,這裏就可以將參數dev所描述的一個framebuffer_device_t結構體轉換成一個fb_context_t結構體ctx。
        參數dev的成員變量common指向了一個hw_device_t結構體,這個結構體的成員變量module指向了一個Gralloc模塊。從前面第1部分的內容可以知道,一個Gralloc模塊是使用一個private_module_t結構體來描述的,因此,我們可以將dev->common.moudle轉換成一個private_module_t結構體m。
        由於private_handle_t結構體hnd所描述的圖形緩衝區可能是在系統幀緩衝區分配的,也有可能是內存中分配的,因此,我們分兩種情況來討論圖形緩衝區渲染過程。
        當private_handle_t結構體hnd所描述的圖形緩衝區是在系統幀緩衝區中分配的時候,即這個圖形緩衝區的標誌值flags的PRIV_FLAGS_FRAMEBUFFER位等於1的時候,我們是不需要將圖形緩衝區的內容拷貝到系統幀緩衝區去的,因爲我們將內容寫入到圖形緩衝區的時候,已經相當於是將內容寫入到了系統幀緩衝區中去了。雖然在這種情況下,我們不需要將圖形緩衝區的內容拷貝到系統幀緩衝區去,但是我們需要告訴系統幀緩衝區設備將要渲染的圖形緩衝區作爲系統當前的輸出圖形緩衝區,這樣纔可以將要渲染的圖形緩衝區的內容繪製到設備顯示屏來。例如,假設系統幀緩衝區有2個圖形緩衝區,當前是以第1個圖形緩衝區作爲輸出圖形緩衝區的,這時候如果我們需要渲染第2個圖形緩衝區,那麼就必須告訴系統幀繪衝區設備,將第2個圖形緩衝區作爲輸出圖形緩衝區。
       設置系統幀緩衝區的當前輸出圖形緩衝區是通過IO控制命令FBIOPUT_VSCREENINFO來進行的。IO控制命令FBIOPUT_VSCREENINFO需要一個fb_var_screeninfo結構體作爲參數。從前面第3部分的內容可以知道,private_module_t結構體m的成員變量info正好保存在我們所需要的這個fb_var_screeninfo結構體。有了個m->info這個fb_var_screeninfo結構體之後,我們只需要設置好它的成員變量yoffset的值(不用設置成員變量xoffset的值是因爲所有的圖形緩衝區的寬度是相等的),就可以將要渲染的圖形緩衝區設置爲系統幀緩衝區的當前輸出圖形緩衝區。fb_var_screeninfo結構體的成員變量yoffset保存的是當前輸出圖形緩衝區在整個系統幀緩衝區的縱向偏移量,即Y偏移量。我們只需要將要渲染的圖形緩衝區的開始地址hnd->base的值減去系統幀緩衝區的基地址m->framebuffer->base的值,再除以圖形緩衝區一行所佔據的字節數m->finfo.line_length,就可以得到所需要的Y偏移量。
        在執行IO控制命令FBIOPUT_VSCREENINFO之前,還會將作爲參數的fb_var_screeninfo結構體的成員變量activate的值設置FB_ACTIVATE_VBL,表示要等到下一個垂直同步事件出現時,再將當前要渲染的圖形緩衝區的內容繪製出來。這樣做的目的是避免出現屏幕閃爍,即避免前後兩個圖形緩衝區的內容各有一部分同時出現屏幕中。
        成功地執行完成IO控制命令FBIOPUT_VSCREENINFO之後,函數還會將當前被渲染的圖形緩衝區保存在private_module_t結構體m的成員變量currentBuffer中,以便可以記錄當前被渲染的圖形緩衝區是哪一個。
        當private_handle_t結構體hnd所描述的圖形緩衝區是在內存中分配的時候,即這個圖形緩衝區的標誌值flags的PRIV_FLAGS_FRAMEBUFFER位等於0的時候,我們就需要將它的內容拷貝到系統幀緩衝區中去了。這個拷貝的工作是通過調用函數memcpy來完成的。在拷貝之前,我們需要三個參數。第一個參數是要渲染的圖形緩衝區的起址地址,這個地址保存在參數buffer所指向的一個private_handle_t結構體中。第二個參數是要系統幀緩衝區的基地址,這個地址保存在private_module_t結構體m的成員變量framebuffer所指向的一個private_handle_t結構體中。第三個參數是要拷貝的內容的大小,這個大小就剛好是一個屏幕像素所佔據的內存的大小。屏幕高度由m->info.yres來描述,而一行屏幕像素所佔用的字節數由m->finfo.line_length來描述,將這兩者相乘,就可以得到一個屏幕像素所佔據的內存的大小。
        在將一塊內存緩衝區的內容拷貝到系統幀緩衝區中去之前,需要對這兩塊緩衝區進行鎖定,以保證在拷貝的過程中,這兩塊緩衝區的內容不會被修改。這個鎖定的工作是由Gralloc模塊中的函數gralloc_lock來實現的。從前面第1部分的內容可以知道,Gralloc模塊中的函數gralloc_lock的地址正好就保存在private_module_t結構體m的成員變量base所描述的一個gralloc_module_t結構體的成員函數lock中。
        在調用函數gralloc_lock來鎖定一塊緩衝區之後,還可以通過最後一個輸出參數來獲得被鎖定的緩衝區的開始地址,因此,通過調用函數gralloc_lock來鎖定要渲染的圖形緩衝區以及系統幀緩衝區,就可以得到前面所需要的第一個和第二個參數。
        將要渲染的圖形緩衝區的內容拷貝到系統幀緩衝區之後,就可以解除前面對它們的鎖定了,這個解鎖的工作是由Gralloc模塊中的函數gralloc_unlock來實現的。從前面第1部分的內容可以知道,Gralloc模塊中的函數gralloc_unlock的地址正好就保存在private_module_t結構體m的成員變量base所描述的一個gralloc_module_t結構體的成員函數unlock中。
        這樣,一個圖形緩衝區的渲染過程就分析完成了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章