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

转载于:https://blog.51cto.com/shyluo/967089

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(8)...相关推荐

  1. Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析

    前面在介绍Android系统的开机画面时提到,Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger服务就是通过向这个帧缓冲区写入内容来绘制应用程序的用户 ...

  2. Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(10)...

    7. 图形缓冲区的注销过程        图形缓冲区使用完成之后,就需要从当前进程中注销.前面提到,注销图形缓冲区是由Gralloc模块中的函数gralloc_unregister_buffer来实现 ...

  3. Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(9)...

             5. 图形缓冲区的释放过程         前面提到,用户空间的应用程序用到的图形缓冲区是由Gralloc模块中的函数gralloc_free来释放的,这个函数实现在文件hardwa ...

  4. Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(2)...

    函数load也是实现在文件hardware/libhardware/hardware.c文件中,如下所示: static int load(const char *id, const char *pa ...

  5. Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(5)...

        3. fb设备的打开过程         在Gralloc模块中,fb设备的ID值定义为GRALLOC_HARDWARE_FB0.GRALLOC_HARDWARE_FB0是一个宏,定义在文件h ...

  6. Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(4)...

            成员变量fd指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存,取决于它的宿主结构体private_handle_t描述的一个图形缓冲区是在帧缓冲区分配的 ...

  7. Android帧布局(Frame Layout)

    Android帧布局(Frame Layout) FrameLayout是最简单的一个布局管理器.FrameLayout为每个加入其中的组件创建一个空白区域(一帧),这些组件根据layout_grav ...

  8. 一篇读懂:Android手机如何通过USB接口与外设通信(附原理分析及方案选型)

    更多技术干货,欢迎扫码关注博主微信公众号:HowieXue,共同探讨软件知识经验,关注就有海量学习资料免费领哦: 目录 0背景 1.手机USB接口通信特点 1.1 使用方便 1.2 通用性强 1.3 ...

  9. Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析

    在前文中,我们分析了SurfaceFlinger服务的启动过程.SurfaceFlinger服务在启动的过程中,会对系统的硬件帧缓冲区进行初始化.由于系统的硬件帧缓冲区一般只有一个,并且不是谁都可以随 ...

最新文章

  1. 还在用 BeanUtils来做对象转换吗?快试试 MapStruct吧
  2. PTA基础编程题目集-7-38 数列求和-加强版
  3. 用cmake编译caffe
  4. mysql 字符集测试_MySQL多字符集备份恢复测试
  5. mysql数据库用户的创建_mysql创建用户及数据库
  6. mysql 聚合函数 怎么用在条件里_MySql 中聚合函数增加条件表达式的方法
  7. JeecgBoot Minio版本6.0.13升级到8.0.3修改方法
  8. python编程一球从100米_Python基础练习实例20(弹球问题)
  9. Split Byte(文件分割助手) v2.4
  10. 红米骁龙855旗舰关键细节曝光:256G ROM+GPU超频模式
  11. Symbian编程总结-基础篇-活动对象正解(1)-理解活动对象
  12. matlab画图函数基本使用(适合新手)
  13. freertos demo2: LED blinky queue 发送消息
  14. Oracle 11gR2光钎链路切换crs服务发生crash
  15. C# .net MVC 微信红包(服务号发送红包)
  16. Vue实现页面导航实战
  17. 六度分离(floyd算法,SPFA算法,最短路—Dijkstra算法)
  18. [高项]消极风险VS积极风险
  19. python有什么颜色_Python中常见颜色记录
  20. vux组件库更换主题颜色的方法

热门文章

  1. winform中ShowInTaskbar设为false时,注册的热键失效的问题解决
  2. (二)设置hexo支持mermaid
  3. vc++ 类 定时器api_关于JS中一些重要的api实现, 巩固你的原生JS功底
  4. sqoop增量导入hive_Sqoop 增量导MySQL数据 至Hive
  5. 请领导审阅并提意见应怎么说_成功的领导是怎样跟下属沟通的?在交谈的时候,应当注意这三点...
  6. tcpip详解有必要看吗_车辆有必要安装“行车记录仪”吗?如何挑选看这里!
  7. 实现语音对讲_什么是五方通话?智慧电梯SIP五方对讲系统详细方案
  8. git 安装_Windows系统Git安装教程(详解Git安装过程)
  9. 职称计算机怎么练,2017职称计算机windows操作训练题
  10. android 动画间隔时间,Android使用View Animation实现动画加载界面