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. // never unmap buffers that were created in this process
  7. private_handle_t* hnd = (private_handle_t*)handle;
  8. if (hnd->pid != getpid()) {
  9. if (hnd->base) {
  10. gralloc_unmap(module, handle);
  11. }
  12. }
  13. return 0;
  14. }
        这个函数同样是首先调用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. fb_context_t* ctx = (fb_context_t*)dev;
  6. private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);
  7. private_module_t* m = reinterpret_cast<private_module_t*>(
  8. dev->common.module);
  9. if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
  10. const size_t offset = hnd->base - m->framebuffer->base;
  11. m->info.activate = FB_ACTIVATE_VBL;
  12. m->info.yoffset = offset / m->finfo.line_length;
  13. if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) {
  14. LOGE("FBIOPUT_VSCREENINFO failed");
  15. m->base.unlock(&m->base, buffer);
  16. return -errno;
  17. }
  18. m->currentBuffer = buffer;
  19. } else {
  20. // If we can't do the page_flip, just copy the buffer to the front
  21. // FIXME: use copybit HAL instead of memcpy
  22. void* fb_vaddr;
  23. void* buffer_vaddr;
  24. m->base.lock(&m->base, m->framebuffer,
  25. GRALLOC_USAGE_SW_WRITE_RARELY,
  26. 0, 0, m->info.xres, m->info.yres,
  27. &fb_vaddr);
  28. m->base.lock(&m->base, buffer,
  29. GRALLOC_USAGE_SW_READ_RARELY,
  30. 0, 0, m->info.xres, m->info.yres,
  31. &buffer_vaddr);
  32. memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
  33. m->base.unlock(&m->base, buffer);
  34. m->base.unlock(&m->base, m->framebuffer);
  35. }
  36. return 0;
  37. }
        参数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中。
        这样,一个图形缓冲区的渲染过程就分析完成了。

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

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

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

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

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

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

       4. 分配图形缓冲区         前面提到,用户空间的应用程序用到的图形缓冲区是由Gralloc模块中的函数gralloc_alloc来分配的,这个函数实现在文件hardware/libha ...

  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. python map(function, iterable, ...) 内置函数的用法 (序列迭代调用并返回)
  2. python 相对路径
  3. C语言位操作--不用中间变量交换两数值
  4. java 重载 不可_深入深刻深到不能再深的理解java中的重载和重写
  5. Spring Boot EasyPoi导出Excel下载
  6. ElasticSearch 全文检索实战
  7. 2018自然语言处理与机器学习论文发表统计
  8. paip.提升用户体验----自定义移位操作符重载
  9. 关于SRE方法论的一些笔记
  10. TPM分析笔记(二)TPM2.0 规范文档
  11. 史上最全的Unreal Engine 4学习资料整理
  12. android apk 微信登入_微信第三方登录(Android 实现)
  13. linux 全选 编辑文本_强烈推荐:Linux终端文本编辑器Micro
  14. 好看的网站发布导航页HTML源码
  15. Python手写实现LDA与QDA算法
  16. 鸿蒙文化博物馆,有趣、有味、有文化!“周末儿童博物馆”在成博欢乐启幕
  17. wordpress更改主页
  18. 【编程工具】程序中出现中文乱码的解决方法
  19. Model Inspector — 软件模型静态规范检查工具
  20. 豆瓣电影影评爬取---最受欢迎的影评[xpath语法]

热门文章

  1. machine learning(15) --Regularization:Regularized logistic regression
  2. IDE to AHCI/RAID 蓝屏补丁
  3. 《SqlServer性能分析一》
  4. [原]请留心asp:Image控件中的ImageUrl属性
  5. 十大编程算法助程序员走上高手之路
  6. 一种基于FPGA 的1080p 高清多摄像头全景视频拼接的泊车(机)
  7. (Kubernetes)k8s和docker的关系
  8. STM32F407的时钟配置
  9. 未来区块链技术将赋能多个领域促进全球经济发展
  10. 苹果手机(ios)拍照上传图片旋转90度问题---java后台处理