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

   4. 分配圖形緩衝區
        前面提到,用戶空間的應用程序用到的圖形緩衝區是由Gralloc模塊中的函數gralloc_alloc來分配的,這個函數實如今文件hardware/libhardware/modules/gralloc/gralloc.cpp中,以下所示:
  
  
  
  
  1. static int gralloc_alloc(alloc_device_t* dev,   
  2.         int w, int h, int format, int usage,   
  3.         buffer_handle_t* pHandle, int* pStride)   
  4. {   
  5.     if (!pHandle || !pStride)   
  6.         return -EINVAL;   
  7.    
  8.     size_t size, stride;   
  9.    
  10.     int align = 4;   
  11.     int bpp = 0;   
  12.     switch (format) {   
  13.         case HAL_PIXEL_FORMAT_RGBA_8888:   
  14.         case HAL_PIXEL_FORMAT_RGBX_8888:   
  15.         case HAL_PIXEL_FORMAT_BGRA_8888:   
  16.             bpp = 4;   
  17.             break;   
  18.         case HAL_PIXEL_FORMAT_RGB_888:   
  19.             bpp = 3;   
  20.             break;   
  21.         case HAL_PIXEL_FORMAT_RGB_565:   
  22.         case HAL_PIXEL_FORMAT_RGBA_5551:   
  23.         case HAL_PIXEL_FORMAT_RGBA_4444:   
  24.             bpp = 2;   
  25.             break;   
  26.         default:   
  27.             return -EINVAL;   
  28.     }   
  29.     size_t bpr = (w*bpp + (align-1)) & ~(align-1);   
  30.     size = bpr * h;   
  31.     stride = bpr / bpp;   
  32.    
  33.     int err;   
  34.     if (usage & GRALLOC_USAGE_HW_FB) {   
  35.         err = gralloc_alloc_framebuffer(dev, size, usage, pHandle);   
  36.     } else {   
  37.         err = gralloc_alloc_buffer(dev, size, usage, pHandle);   
  38.     }   
  39.    
  40.     if (err < 0) {   
  41.         return err;   
  42.     }   
  43.    
  44.     *pStride = stride;   
  45.     return 0;   
  46. }   
      參數format用來描述要分配的圖形緩衝區的顏色格式。當format值等於HAL_PIXEL_FORMAT_RGBA_888八、HAL_PIXEL_FORMAT_RGBX_8888或者HAL_PIXEL_FORMAT_BGRA_8888的時候,一個像素須要使用32位來表示,即4個字節。當format值等於HAL_PIXEL_FORMAT_RGB_888的時候,一個像素須要使用24位來描述,即3個字節。當format值等於HAL_PIXEL_FORMAT_RGB_56五、HAL_PIXEL_FORMAT_RGBA_5551或者HAL_PIXEL_FORMAT_RGBA_4444的時候,一個像須要使用16位來描述,即2個字節。最終一個像素須要使用的字節數保存在變量bpp中。

 
        參數w表示要分配的圖形緩衝區所保存的圖像的寬度,將它乘以bpp,就能夠獲得保存一行像素所須要使用的字節數。咱們須要將這個字節數對齊到4個字節邊界,最後獲得一行像素所須要的字節數就保存在變量bpr中。
        參數h表示要分配的圖形緩衝區所保存的圖像的高度,將它乘以bpr,就能夠獲得保存整個圖像所須要使用的字節數。
        將變量bpr的值除以變量bpp的值,就獲得要分配的圖形緩衝區一行包含有多少個像素點,這個結果須要保存在輸出參數pStride中,以即可以返回給調用者。
        參數usage用來描述要分配的圖形緩衝區的用途。若是是用來在系統幀緩衝區中渲染的,即參數usage的GRALLOC_USAGE_HW_FB位等於1,那麼就必需要系統幀緩衝區中分配,不然的話,就在內存中分配。注意,在內存中分配的圖形緩衝區,最終是須要拷貝到系統幀緩衝區去的,以即可以將它所描述的圖形渲染出來。
        函數gralloc_alloc_framebuffer用來在系統幀緩衝區中分配圖形緩衝區,而函數gralloc_alloc_buffer用來在內存在分配圖形緩衝區,接下來咱們就分別分析這兩個函數的實現。
         函數gralloc_alloc_framebuffer實如今文件hardware/libhardware/modules/gralloc/gralloc.cpp中,以下所示:
  1. static int gralloc_alloc_framebuffer(alloc_device_t* dev,  
  2.         size_t size, int usage, buffer_handle_t* pHandle)  
  3. {  
  4.     private_module_t* m = reinterpret_cast<private_module_t*>(  
  5.             dev->common.module);  
  6.     pthread_mutex_lock(&m->lock);  
  7.     int err = gralloc_alloc_framebuffer_locked(dev, size, usage, pHandle);  
  8.     pthread_mutex_unlock(&m->lock);  
  9.     return err;  
  10. }  

        這個函數調用了另一個函數gralloc_alloc_framebuffer_locked來分配圖形緩衝區。

 
        函數gralloc_alloc_framebuffer_locked也是實如今文件hardware/libhardware/modules/gralloc/gralloc.cpp中,以下所示:
  
  
  
  
  1. static int gralloc_alloc_framebuffer_locked(alloc_device_t* dev,   
  2.         size_t sizeint usage, buffer_handle_t* pHandle)   
  3. {   
  4.     private_module_t* m = reinterpret_cast<private_module_t*>(   
  5.             dev->common.module);   
  6.    
  7.     // allocate the framebuffer   
  8.     if (m->framebuffer == NULL) {   
  9.         // initialize the framebuffer, the framebuffer is mapped once   
  10.         // and forever.   
  11.         int err = mapFrameBufferLocked(m);   
  12.         if (err < 0) {   
  13.             return err;   
  14.         }   
  15.     }   
  16.    
  17.     const uint32_t bufferMask = m->bufferMask;   
  18.     const uint32_t numBuffers = m->numBuffers;   
  19.     const size_t bufferSize = m->finfo.line_length * m->info.yres;   
  20.     if (numBuffers == 1) {   
  21.         // If we have only one buffer, we never use page-flipping. Instead,   
  22.         // we return a regular buffer which will be memcpy'ed to the main   
  23.         // screen when post is called.   
  24.         int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D;   
  25.         return gralloc_alloc_buffer(dev, bufferSize, newUsage, pHandle);   
  26.     }   
  27.    
  28.     if (bufferMask >= ((1LU<<numBuffers)-1)) {   
  29.         // We ran out of buffers.   
  30.         return -ENOMEM;   
  31.     }   
  32.    
  33.     // create a "fake" handles for it   
  34.     intptr_t vaddr = intptr_t(m->framebuffer->base);   
  35.     private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size,   
  36.             private_handle_t::PRIV_FLAGS_FRAMEBUFFER);   
  37.    
  38.     // find a free slot   
  39.     for (uint32_t i=0 ; i<numBuffers ; i++) {   
  40.         if ((bufferMask & (1LU<<i)) == 0) {   
  41.             m->bufferMask |= (1LU<<i);   
  42.             break;   
  43.         }   
  44.         vaddr += bufferSize;   
  45.     }   
  46.    
  47.     hnd->base = vaddr;   
  48.     hnd->offset = vaddr - intptr_t(m->framebuffer->base);   
  49.     *pHandle = hnd;   
  50.    
  51.     return 0;   
  52. }   

       在系統幀緩衝區分配圖形緩衝區以前,首先要對系統幀緩衝區進行過初始化,即這裏的變量m所指向的一個private_module_t結構體的成員變量framebuffer的值不能等於NULL。若是等於NULL的話,那麼就必需要調用另一個函數mapFrameBufferLocked來初始化系統幀緩衝區。初始化系統幀緩衝區的過程能夠參考前面第3部分的內容。

 
       變量bufferMask用來描述系統幀緩衝區的使用狀況,而變量numBuffers用來描述系統幀緩衝區能夠劃分爲多少個圖形緩衝區來使用,另一個變量bufferSize用來描述設備顯示屏一屏內容所佔用的內存的大小。
        若是系統幀緩衝區只有一個圖形緩衝區大小,即變量numBuffers的值等於1,那麼這個圖形緩衝區就始終用做系統主圖形緩衝區來使用。在這種狀況下,咱們就不可以在系統幀緩衝區中分配圖形緩衝區來給用戶空間的應用程序使用,所以,這時候就會轉向內存中來分配圖形緩衝區,即調用函數gralloc_alloc_buffer來分配圖形緩衝區。注意,這時候分配的圖形緩衝區的大小爲一屏內容的大小,即bufferSize。
        若是bufferMask的值大於等於((1LU<<numBuffers)-1)的值,那麼就說明系統幀緩衝區中的圖形緩衝區所有都分配出去了,這時候分配圖形緩衝區就失敗了。例如,假設圖形緩衝區的個數爲2,那麼((1LU<<numBuffers)-1)的值就等於3,即二制制0x11。若是這時候bufferMask的值也等於0x11,那麼就表示第一個和第二個圖形緩衝區都已經分配出去了。所以,這時候就不能再在系統幀緩衝區中分配圖形緩衝區。
       假設此時系統幀緩衝區中尚有空閒的圖形緩衝區的,接下來函數就會建立一個private_handle_t結構體hnd來描述這個即將要分配出去的圖形緩衝區。注意,這個圖形緩衝區的標誌值等於PRIV_FLAGS_FRAMEBUFFER,即表示這是一塊在系統幀緩衝區中分配的圖形緩衝區。
       接下來的for循環從低位到高位檢查變量bufferMask的值,而且找到第一個值等於0的位,這樣就能夠知道在系統幀緩衝區中,第幾個圖形緩衝區的是空閒的。注意,變量vadrr的值開始的時候指向系統幀緩衝區的基地址,在下面的for循環中,每循環一次它的值都會增長bufferSize。從這裏就能夠看出,每次從系統幀緩衝區中分配出去的圖形緩衝區的大小都是恰好等於顯示屏一屏內容大小的。
        最後分配出去的圖形緩衝區的開始地址就保存在前面所建立的private_handle_t結構體hnd的成員變量base中,這樣,用戶空間的應用程序就能夠直接將要渲染的圖形內容拷貝到這個地址上去,這就至關因而直接將圖形渲染到系統幀緩衝區中去。
        在將private_handle_t結構體hnd返回給調用者以前,還須要設置它的成員變量offset,以即可以知道它所描述的圖形緩衝區的起始地址相對於系統幀緩衝區的基地址的偏移量。
        至此,在系統幀緩衝區中分配圖形緩衝區的過程就分析完成了,接下來咱們再分析在內存在分析圖形緩衝區的過程,即分析函數gralloc_alloc_buffer的實現。
        函數gralloc_alloc_buffer也是實如今文件hardware/libhardware/modules/gralloc/gralloc.cpp中,以下所示:
  
  
  
  
  1. static int gralloc_alloc_buffer(alloc_device_t* dev,   
  2.         size_t sizeint usage, buffer_handle_t* pHandle)   
  3. {   
  4.     int err = 0;   
  5.     int fd = -1;   
  6.    
  7.     size = roundUpToPageSize(size);   
  8.    
  9.     fd = ashmem_create_region("gralloc-buffer"size);   
  10.     if (fd < 0) {   
  11.         LOGE("couldn't create ashmem (%s)", strerror(-errno));   
  12.         err = -errno;   
  13.     }   
  14.    
  15.     if (err == 0) {   
  16.         private_handle_t* hnd = new private_handle_t(fd, size, 0);   
  17.         gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(   
  18.                 dev->common.module);   
  19.         err = mapBuffer(module, hnd);   
  20.         if (err == 0) {   
  21.             *pHandle = hnd;   
  22.         }   
  23.     }   
  24.    
  25.     LOGE_IF(err, "gralloc failed err=%s", strerror(-err));   
  26.    
  27.     return err;   
  28. }   

       這個函數的實現很簡單,它首先調用函數ashmem_create_region來建立一塊匿名共享內存,接着再在這塊匿名共享內存上分配一個圖形緩衝區。注意,這個圖形緩衝區也是使用一個private_handle_t結構體來描述的,不過這個圖形緩衝區的標誌值等於0,以區別於在系統幀緩衝區中分配的圖形緩衝區。匿名共享內存的相關知識,能夠參考前面 Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃 一文,以及 Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析 這篇文章。

 
        從匿名共享內存中分配的圖形緩衝區還須要映射到進程的地址空間來,而後纔可使用,這是經過調用函數mapBuffer來實現的。
        函數mapBuffer實如今文件hardware/libhardware/modules/gralloc/mapper.cpp中,以下所示:
  1. int mapBuffer(gralloc_module_t const* module,  
  2.         private_handle_t* hnd)  
  3. {  
  4.     void* vaddr;  
  5.     return gralloc_map(module, hnd, &vaddr);  
  6. }  

       它經過調用另一個函數gralloc_map來將參數hnd所描述的一個圖形緩衝區映射到當前進程的地址空間來。後面在分析圖形緩衝區的註冊過程時,咱們再分析函數gralloc_map的實現。

 
        注意,在Android系統中,在系統幀緩衝區中分配的圖形緩衝區是在SurfaceFlinger服務中使用的,而在內存中分配的圖形緩衝區既能夠在SurfaceFlinger服務中使用,也能夠在其它的應用程序中使用。當其它的應用程序須要使用圖形緩衝區的時候,它們就會請求SurfaceFlinger服務爲它們分配,所以,對於其它的應用程序來講,它們只須要將SurfaceFlinger服務返回來的圖形緩衝區映射到本身的進程地址空間來使用就能夠了,這就是後面咱們所要分析的圖形緩衝區的註冊過程。
        至此,圖形緩衝區的分配過程就分析完成了,接下來咱們繼續分析圖形緩衝區的釋放過程。
相關文章
相關標籤/搜索