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

        7. 图形缓冲区的注销过程app

       图形缓冲区使用完成以后,就须要从当前进程中注销。前面提到,注销图形缓冲区是由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中。
        这样,一个图形缓冲区的渲染过程就分析完成了。
相关文章
相关标签/搜索