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_8888、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_565、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服务返回来的图形缓冲区映射到自己的进程地址空间来使用就可以了,这就是后面我们所要分析的图形缓冲区的注册过程。
        至此,图形缓冲区的分配过程就分析完成了,接下来我们继续分析图形缓冲区的释放过程。




本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967089,如需转载请自行联系原作者
目录
相关文章
|
1月前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
3月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
51 2
|
2月前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
67 15
Android 系统缓存扫描与清理方法分析
|
2月前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
3月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
145 3
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
3月前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
4月前
|
存储 编解码 网络协议
Android平台GB28181执法记录仪硬件选型和国标技术实现探讨
前几年,我们在做Android平台GB28181设备接入模块的时候,第一个使用场景想到的就是用在公检法应急指挥等场景下的执法记录仪,本篇blog,我们主要围绕Android平台GB28181执法记录仪的硬件选型、设备接入、音视频流配置、流媒体传输、存储和管理、控制与控制中心等方面进行设计,探讨下Android平台GB28181设备接入模块在执法记录仪行业的应用。
Android平台GB28181执法记录仪硬件选型和国标技术实现探讨
|
4月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着重要的位置。本文将深入探讨这两种操作系统的开发环境,从编程语言到开发工具,从用户界面设计到性能优化,以及市场趋势对开发者选择的影响。我们旨在为读者提供一个全面的比较视角,帮助理解不同平台的优势与挑战,并为那些站在选择十字路口的开发者提供有价值的参考信息。
104 17
|
3月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
52 1