变量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中,如下所示:
- static int gralloc_alloc_buffer(alloc_device_t* dev,
- size_t size, int usage, buffer_handle_t* pHandle)
- {
- int err = 0;
- int fd = -1;
- size = roundUpToPageSize(size);
- fd = ashmem_create_region("gralloc-buffer", size);
- if (fd < 0) {
- LOGE("couldn't create ashmem (%s)", strerror(-errno));
- err = -errno;
- }
- if (err == 0) {
- private_handle_t* hnd = new private_handle_t(fd, size, 0);
- gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
- dev->common.module);
- err = mapBuffer(module, hnd);
- if (err == 0) {
- *pHandle = hnd;
- }
- }
- LOGE_IF(err, "gralloc failed err=%s", strerror(-err));
- return err;
- }
这个函数的实现很简单,它首先调用函数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中,如下所示:
- int mapBuffer(gralloc_module_t const* module,
- private_handle_t* hnd)
- {
- void* vaddr;
- return gralloc_map(module, hnd, &vaddr);
- }
它通过调用另外一个函数gralloc_map来将参数hnd所描述的一个图形缓冲区映射到当前进程的地址空间来。后面在分析图形缓冲区的注册过程时,我们再分析函数gralloc_map的实现。
注意,在Android系统中,在系统帧缓冲区中分配的图形缓冲区是在SurfaceFlinger服务中使用的,而在内存中分配的图形缓冲区既可以在SurfaceFlinger服务中使用,也可以在其它的应用程序中使用。当其它的应用程序需要使用图形缓冲区的时候,它们就会请求SurfaceFlinger服务为它们分配,因此,对于其它的应用程序来说,它们只需要将SurfaceFlinger服务返回来的图形缓冲区映射到自己的进程地址空间来使用就可以了,这就是后面我们所要分析的图形缓冲区的注册过程。
至此,图形缓冲区的分配过程就分析完成了,接下来我们继续分析图形缓冲区的释放过程。
5. 图形缓冲区的释放过程
前面提到,用户空间的应用程序用到的图形缓冲区是由Gralloc模块中的函数gralloc_free来释放的,这个函数实现在文件hardware/libhardware/modules/gralloc/gralloc.cpp中,如下所示:
- static int gralloc_free(alloc_device_t* dev,
- buffer_handle_t handle)
- {
- if (private_handle_t::validate(handle) < 0)
- return -EINVAL;
- private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(handle);
- if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
- // free this buffer
- private_module_t* m = reinterpret_cast<private_module_t*>(
- dev->common.module);
- const size_t bufferSize = m->finfo.line_length * m->info.yres;
- int index = (hnd->base - m->framebuffer->base) / bufferSize;
- m->bufferMask &= ~(1<<index);
- } else {
- gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
- dev->common.module);
- terminateBuffer(module, const_cast<private_handle_t*>(hnd));
- }
- close(hnd->fd);
- delete hnd;
- return 0;
- }
要释放的图形缓冲区使用参数handle来描述。前面提到,从Gralloc模块中分配的图形缓冲区是使用private_handle_t结构体来描述的,因此,这里的参数handle应该指向一个private_handle_t结构体,这是通过调用private_handle_t类的静态成员函数validate来验证的。private_handle_t类的静态成员函数validate的实现可以参考前面第1部分的内容。
要释放的图形缓冲区有可能是在系统帧缓冲区分配的,也有可能是在内存中分配的,这可以通过检查它的标志值flags的PRIV_FLAGS_FRAMEBUFFER位是否等于1来确认。
如果要释放的图形缓冲区是在系统帧缓冲区中分配的,那么首先要知道这个图形缓冲区是系统帧缓冲区的第index个位置,接着再将变量m所描述的一个private_module_t结构体的成员变量bufferMask的第index位重置为0即可。我们只需要将要释放的图形缓冲区的开始地址减去系统帧缓冲区的基地址,再除以一个图形缓冲区的大小,就可以知道要释放的图形缓冲区是系统帧缓冲区的第几个位置。这个过程刚好是在系统帧缓冲区中分配图形缓冲区的逆操作。
如果要释放的图形缓冲区是内存中分配的,那么只需要调用另外一个函数terminateBuffer来解除要释放的图形缓冲区在当前进程的地址空间中的映射。
最后,这个函数还会将用来描述要释放的图形缓冲区的private_handle_t结构体所占用的内存释放掉,并且将要要释放的图形缓冲区所在的系统帧缓冲区或者匿名共享内存的文件描述符关闭掉。
函数terminateBuffer实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
- int terminateBuffer(gralloc_module_t const* module,
- private_handle_t* hnd)
- {
- if (hnd->base) {
- // this buffer was mapped, unmap it now
- gralloc_unmap(module, hnd);
- }
- return 0;
- }
它通过调用另外一个函数gralloc_unmap来解除参数hnd所描述的一个图形缓冲区在当前进程的地址空间中的映射。后面在分析图形缓冲区的注销过程时,我们再详细分析函数gralloc_unmap的实现。
至此,图形缓冲区的释放过程就分析完成了,接下来我们继续分析图形缓冲区的注册过程。
6. 图形缓冲区的注册过程
前面提到,在Android系统中,所有的图形缓冲区都是由SurfaceFlinger服务分配的,而当一个图形缓冲区被分配的时候,它会同时被映射到请求分配的进程的地址空间去,即分配的过程同时也包含了注册的过程。但是对用户空间的其它的应用程序来说,它们所需要的图形缓冲区是在由SurfaceFlinger服务分配的,因此,当它们得到SurfaceFlinger服务分配的图形缓冲区之后,还需要将这块图形缓冲区映射到自己的地址空间来,以便可以使用这块图形缓冲区。这个映射的过程即为我们接下来要分析的图形缓冲区注册过程。
前面还提到,注册图形缓冲区的操作是由Gralloc模块中的函数gralloc_register_buffer来实现的,这个函数实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
- int gralloc_register_buffer(gralloc_module_t const* module,
- buffer_handle_t handle)
- {
- if (private_handle_t::validate(handle) < 0)
- return -EINVAL;
- // if this handle was created in this process, then we keep it as is.
- int err = 0;
- private_handle_t* hnd = (private_handle_t*)handle;
- if (hnd->pid != getpid()) {
- void *vaddr;
- err = gralloc_map(module, handle, &vaddr);
- }
- return err;
- }
这个函数首先验证参数handle指向的一块图形缓冲区的确是由Gralloc模块分配的,方法是调用private_handle_t类的静态成员函数validate来验证,即如果参数handle指向的是一个private_handle_t结构体,那么它所指向的一块图形缓冲区就是由Gralloc模块分配的。
通过了上面的检查之后,函数gralloc_register_buffer还需要检查当前进程是否就是请求Gralloc模块分配图形缓冲区hnd的进程。如果是的话,那么当前进程在请求Gralloc模块分配图形缓冲区hnd的时候,就已经将图形缓冲区hnd映射进自己的地址空间来了,因此,这时候就不需要重复在当前进程中注册这个图形缓冲区。
真正执行注册图形缓冲区的操作是由函数gralloc_map来实现的,这个函数也是实现文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
- static int gralloc_map(gralloc_module_t const* module,
- buffer_handle_t handle,
- void** vaddr)
- {
- private_handle_t* hnd = (private_handle_t*)handle;
- if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
- size_t size = hnd->size;
- void* mappedAddress = mmap(0, size,
- PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);
- if (mappedAddress == MAP_FAILED) {
- LOGE("Could not mmap %s", strerror(errno));
- return -errno;
- }
- hnd->base = intptr_t(mappedAddress) + hnd->offset;
- //LOGD("gralloc_map() succeeded fd=%d, off=%d, size=%d, vaddr=%p",
- // hnd->fd, hnd->offset, hnd->size, mappedAddress);
- }
- *vaddr = (void*)hnd->base;
- return 0;
- }
由于在系统帧缓冲区中分配的图形缓冲区只在SurfaceFlinger服务中使用,而SurfaceFlinger服务在初始化系统帧缓冲区的时候,已经将系统帧缓冲区映射到自己所在的进程中来了,因此,函数gralloc_map如果发现要注册的图形缓冲区是在系统帧缓冲区分配的时候,那么就不需要再执行映射图形缓冲区的操作了。
如果要注册的图形缓冲区是在内存中分配的,即它的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于1,那么接下来就需要将它映射到当前进程的地址空间来了。由于要注册的图形缓冲区是在文件描述符hnd->fd所描述的一块匿名共享内存中分配的,因此,我们只需要将文件描述符hnd->fd所描述的一块匿名共享内存映射到当前进程的地址空间来,就可以将参数hnd所描述的一个图形缓冲区映射到当前进程的地址空间来。
由于映射文件描述符hnd->fd得到的是一整块匿名共享内存在当前进程地址空间的基地址,而要注册的图形缓冲区可能只占据这块匿名共享内存的某一小部分,因此,我们还需要将要注册的图形缓冲区的在被映射的匿名共享内存中的偏移量hnd->offset加上被映射的匿名共享内存的基地址hnd->base,才可以得到要注册的图形缓冲区在当前进程中的访问地址,这个地址最终又被写入到hnd->base中去。
注册图形缓冲区的过程就是这么简单,接下来我们再分析图形缓冲区的注销过程。
7. 图形缓冲区的注销过程
图形缓冲区使用完成之后,就需要从当前进程中注销。前面提到,注销图形缓冲区是由Gralloc模块中的函数gralloc_unregister_buffer来实现的,这个函数实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
- int gralloc_unregister_buffer(gralloc_module_t const* module,
- buffer_handle_t handle)
- {
- if (private_handle_t::validate(handle) < 0)
- return -EINVAL;
- // never unmap buffers that were created in this process
- private_handle_t* hnd = (private_handle_t*)handle;
- if (hnd->pid != getpid()) {
- if (hnd->base) {
- gralloc_unmap(module, handle);
- }
- }
- return 0;
- }
这个函数同样是首先调用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中,如下所示:
- static int gralloc_unmap(gralloc_module_t const* module,
- buffer_handle_t handle)
- {
- private_handle_t* hnd = (private_handle_t*)handle;
- if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
- void* base = (void*)hnd->base;
- size_t size = hnd->size;
- //LOGD("unmapping from %p, size=%d", base, size);
- if (munmap(base, size) < 0) {
- LOGE("Could not unmap %s", strerror(errno));
- }
- }
- hnd->base = 0;
- return 0;
- }
这个函数的实现与前面所分析的函数gralloc_map的实现是类似的,只不过它执行的是相反的操作,即将解除一个指定的图形缓冲区在当前进程的地址空间中的映射,从而完成对这个图形缓冲区的注销工作。
这样,图形缓冲区的注销过程就分析完成了,接下来我们再继续分析一个图形缓冲区是如何被渲染到系统帧缓冲区去的,即它的内容是如何绘制在设备显示屏中的。
8. 图形缓冲区的渲染过程
用户空间的应用程序将画面内容写入到图形缓冲区中去之后,还需要将图形缓冲区渲染到系统帧缓冲区中去,这样才可以把画面绘制到设备显示屏中去。前面提到,渲染图形缓冲区是由Gralloc模块中的函数fb_post来实现的,这个函数实现在文件hardware/libhardware/modules/gralloc/framebuffer.cpp中,如下所示:
- static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)
- {
- if (private_handle_t::validate(buffer) < 0)
- return -EINVAL;
- fb_context_t* ctx = (fb_context_t*)dev;
- private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);
- private_module_t* m = reinterpret_cast<private_module_t*>(
- dev->common.module);
- if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
- const size_t offset = hnd->base - m->framebuffer->base;
- m->info.activate = FB_ACTIVATE_VBL;
- m->info.yoffset = offset / m->finfo.line_length;
- if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) {
- LOGE("FBIOPUT_VSCREENINFO failed");
- m->base.unlock(&m->base, buffer);
- return -errno;
- }
- m->currentBuffer = buffer;
- } else {
- // If we can't do the page_flip, just copy the buffer to the front
- // FIXME: use copybit HAL instead of memcpy
- void* fb_vaddr;
- void* buffer_vaddr;
- m->base.lock(&m->base, m->framebuffer,
- GRALLOC_USAGE_SW_WRITE_RARELY,
- 0, 0, m->info.xres, m->info.yres,
- &fb_vaddr);
- m->base.lock(&m->base, buffer,
- GRALLOC_USAGE_SW_READ_RARELY,
- 0, 0, m->info.xres, m->info.yres,
- &buffer_vaddr);
- memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
- m->base.unlock(&m->base, buffer);
- m->base.unlock(&m->base, m->framebuffer);
- }
- return 0;
- }
参数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中。
这样,一个图形缓冲区的渲染过程就分析完成了。
为了完整性起见,最后我们再简要分析函数gralloc_lock和gralloc_unlock的实现,以便可以了解一个图形缓冲区的锁定和解锁操作是如何实现的。
函数gralloc_lock实现在文件hardware/libhardware/modules/gralloc/mapper.cpp文件中,如下所示:
- int gralloc_lock(gralloc_module_t const* module,
- buffer_handle_t handle, int usage,
- int l, int t, int w, int h,
- void** vaddr)
- {
- // this is called when a buffer is being locked for software
- // access. in thin implementation we have nothing to do since
- // not synchronization with the h/w is needed.
- // typically this is used to wait for the h/w to finish with
- // this buffer if relevant. the data cache may need to be
- // flushed or invalidated depending on the usage bits and the
- // hardware.
- if (private_handle_t::validate(handle) < 0)
- return -EINVAL;
- private_handle_t* hnd = (private_handle_t*)handle;
- *vaddr = (void*)hnd->base;
- return 0;
- }
从这里可以看出,函数gralloc_lock其实并没有执行锁定参数handle所描述的一个缓冲区的操作,它只简单地将要锁定的缓冲区的开始地址返回给调用者。
理论上来说,函数gralloc_lock应该检查参数handle所描述的一个缓冲区是否正在被其进程或者线程使用。如果是的话,那么函数gralloc_lock就必须要等待,直到要锁定的缓冲区被其它进程或者线程使用结束为止,以便接下来可以独占它。由于函数gralloc_lock实际上并没有作这些操作,因此,就必须要由调用者来保证要锁定的缓冲区当前是没有被其它进程或者线程使用的。
函数gralloc_unlock也是实现在文件hardware/libhardware/modules/gralloc/mapper.cpp文件中,如下所示:
- int gralloc_unlock(gralloc_module_t const* module,
- buffer_handle_t handle)
- {
- // we're done with a software buffer. nothing to do in this
- // implementation. typically this is used to flush the data cache.
- if (private_handle_t::validate(handle) < 0)
- return -EINVAL;
- return 0;
- }
函数gralloc_unlock执行的操作本来是刚好与函数gralloc_lock相反的,但是由于函数gralloc_lock并没有真实地锁定参数handle所描述的一个缓冲区的,因此,函数gralloc_unlock是不需要执行实际的解锁工作的。
至此,我们就分析完成Android帧缓冲区硬件抽象层模块Gralloc的实现原理了。从分析的过程可以知道,为了在屏幕中绘制一个指定的画面,我们需要:
1. 分配一个匹配屏幕大小的图形缓冲区
2. 将分配好的图形缓冲区注册(映射)到当前进程的地址空间来
3. 将要绘制的画面的内容写入到已经注册好的图形缓冲区中去,并且渲染(拷贝)到系统帧缓冲区中去
为了实现以上三个操作,我们还需要:
1. 加载Gralloc模块
2. 打开Gralloc模块中的gralloc设备和fb设备
其中,gralloc设备负责分配图形缓冲区,Gralloc模块负责注册图形缓冲区,而fb设备负责渲染图形缓冲区。
理解了Gralloc模块的实现原理之后,就可以为后续分析SurfaceFlinger服务的实现打下坚实的基础了。
完!
本文转自:http://blog.csdn.net/luoshengyang/article/details/7747932