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

简介:
     函数fb_device_open在打开fb设备的过程中,会调用另外一个函数mapFrameBuffer来获得系统帧缓冲区的信息,并且将这些信息保存在参数module所描述的一个private_module_t结构体的各个成员变量中。有了系统帧缓冲区的信息之后,函数fb_device_open接下来就可以对前面所打开的一个fb设备的各个成员变量进行初始化。这些成员变量的含义可以参考前面对结构体framebuffer_device_t的介绍。接下来我们只简单介绍一下结构体framebuffer_device_t的成员变量stride和format的初始化过程。
        变量m的成员变量finfo的类型为fb_fix_screeninfo,它是在函数mapFrameBuffer中被始化的。fb_fix_screeninfo是在内核中定义的一个结构体,用来描述设备显示屏的固定属性信息,其中,它的成员变量line_length用来描述显示屏一行像素总共所占用的字节数。
        变量m的另外一个成员变量info的类型为fb_var_screeninfo,它也是在函数mapFrameBuffer中被始化的。fb_var_screeninfo也是内核中定义的一个结构体,用来描述可以动态设置的显示屏属性信息,其中,它的成员变量bits_per_pixel用来描述显示屏每一个像素所占用的位数。
        这样,我们将m->info.bits_per_pixel的值向右移3位,就可以得到显示屏每一个像素所占用的字节数。用显示屏每一个像素所占用的字节数去除显示屏一行像素总共所占用的字节数m->finfo.line_length,就可以得到显示屏一行有多少个像素点。这个值最终就可以保存在前面所打开的fb设备的成员变量stride中。
        当显示屏每一个像素所占用的位数等于32的时候,那么前面所打开的fb设备的像素格式format就会被设置为HAL_PIXEL_FORMAT_RGBX_8888,否则的话,就会被设置为HAL_PIXEL_FORMAT_RGB_565。另一方面,如果在编译的时候定义了NO_32BPP宏,即不要使用32位来描述一个像素,那么函数fb_device_open就会强制将前面所打开的fb设备的像素格式format设置为HAL_PIXEL_FORMAT_RGB_565。
        函数mapFrameBuffer除了用来获得系统帧缓冲区的信息之外,还会将系统帧缓冲区映射到当前进程的地址空间来。在Android系统中,Gralloc模块中的fb设备是由SurfaceFlinger服务来负责打开和管理的,而SurfaceFlinger服是运行System进程中的,因此,系统帧缓冲区实际上是映射到System进程的地址空间中的。
        函数mapFrameBuffer实现在文件hardware/libhardware/modules/gralloc/framebuffer.cpp,如下所示:
  1. static int mapFrameBuffer(struct private_module_t* module)  
  2. {  
  3.     pthread_mutex_lock(&module->lock);  
  4.     int err = mapFrameBufferLocked(module);  
  5.     pthread_mutex_unlock(&module->lock);  
  6.     return err;  
  7. }  
       这个函数调用了同一个文件中的另外一个函数mapFrameBufferLocked来初始化参数module以及将系统帧缓冲区映射到当前进程的地址空间来。
 
       函数mapFrameBufferLocked的实现比较长,我们分段来阅读:
  1. int mapFrameBufferLocked(struct private_module_t* module)  
  2. {  
  3.     // already initialized...  
  4.     if (module->framebuffer) {  
  5.         return 0;  
  6.     }  
  7.   
  8.     char const * const device_template[] = {  
  9.             "/dev/graphics/fb%u",  
  10.             "/dev/fb%u",  
  11.             0 };  
  12.   
  13.     int fd = -1;  
  14.     int i=0;  
  15.     char name[64];  
  16.   
  17.     while ((fd==-1) && device_template[i]) {  
  18.         snprintf(name, 64, device_template[i], 0);  
  19.         fd = open(name, O_RDWR, 0);  
  20.         i++;  
  21.     }  
  22.     if (fd < 0)  
  23.         return -errno;  

        这段代码在首先在系统中检查是否存在设备文件/dev/graphics/fb0或者/dev/fb0。如果存在的话,那么就调用函数open来打开它,并且将得到的文件描述符保存在变量fd中。这样,接下来函数mapFrameBufferLocked就可以通过文件描述符fd来与内核中的帧缓冲区驱动程序交互。
 
        继续往下看函数mapFrameBufferLocked:
  1. struct fb_fix_screeninfo finfo;  
  2. if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)  
  3.     return -errno;  
  4.   
  5. struct fb_var_screeninfo info;  
  6. if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)  
  7.     return -errno;  
       这几行代码分别通过IO控制命令FBIOGET_FSCREENINFO和FBIOGET_VSCREENINFO来获得系统帧缓冲区的信息,分别保存在fb_fix_screeninfo结构体finfo和fb_var_screeninfo结构体info中。
 
        再往下看函数mapFrameBufferLocked:
  1.     info.reserved[0] = 0;  
  2.     info.reserved[1] = 0;  
  3.     info.reserved[2] = 0;  
  4.     info.xoffset = 0;  
  5.     info.yoffset = 0;  
  6.     info.activate = FB_ACTIVATE_NOW;  
  7.   
  8. #if defined(NO_32BPP)  
  9.     /* 
  10.      * Explicitly request 5/6/5 
  11.      */  
  12.     info.bits_per_pixel = 16;  
  13.     info.red.offset     = 11;  
  14.     info.red.length     = 5;  
  15.     info.green.offset   = 5;  
  16.     info.green.length   = 6;  
  17.     info.blue.offset    = 0;  
  18.     info.blue.length    = 5;  
  19.     info.transp.offset  = 0;  
  20.     info.transp.length  = 0;  
  21. #endif  
  22.   
  23.     /* 
  24.      * Request NUM_BUFFERS screens (at lest 2 for page flipping) 
  25.      */  
  26.     info.yres_virtual = info.yres * NUM_BUFFERS;  
  27.   
  28.   
  29.     uint32_t flags = PAGE_FLIP;  
  30.     if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {  
  31.         info.yres_virtual = info.yres;  
  32.         flags &= ~PAGE_FLIP;  
  33.         LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");  
  34.     }  
  35.   
  36.     if (info.yres_virtual < info.yres * 2) {  
  37.         // we need at least 2 for page-flipping  
  38.         info.yres_virtual = info.yres;  
  39.         flags &= ~PAGE_FLIP;  
  40.         LOGW("page flipping not supported (yres_virtual=%d, requested=%d)",  
  41.                 info.yres_virtual, info.yres*2);  
  42.     }  
       这段代码主要是用来设置设备显示屏的虚拟分辨率。在前面 Android系统的开机画面显示过程分析 一文提到,结构体fb_var_screeninfo的成员变量xres和yres用来描述显示屏的可视分辨率,而成员变量xres_virtual和yres_virtual用来描述显示屏的虚拟分辨率。这里保持可视分辨率以及虚拟分辨率的宽度值不变,而将虚拟分辨率的高度值设置为可视分辨率的高度值的NUM_BUFFERS倍。NUM_BUFFERS是一个宏,它的值被定义为2。这样,我们就可以将系统帧缓冲区划分为两个图形缓冲区来使用,即可以通过硬件来实现双缓冲技术。
 
       在结构体fb_var_screeninfo中,与显示屏的可视分辨率和虚拟分辨率相关的另外两个成员变量是xoffset和yoffset,它们用来告诉帧缓冲区当前要渲染的图形缓冲区是哪一个,它们的使用方法可以参考前面 Android系统的开机画面显示过程分析 一文。
       这段代码在设置设备显示屏的虚拟分辨率之前,还会检查是否定义了宏NO_32BPP。如果定义了的话,那么就说明系统显式地要求将帧缓冲区的像素格式设置为HAL_PIXEL_FORMAT_RGB_565。在这种情况下,这段代码就会通过fb_var_screeninfo结构体info的成员变量bits_per_pixel、red、green、blue和transp来通知帧缓冲区驱动程序使用HAL_PIXEL_FORMAT_RGB_565像素格式来渲染显示屏。
        这段代码最终是通过IO控制命令FBIOPUT_VSCREENINFO来设置设备显示屏的虚拟分辨率以及像素格式的。如果设置失败,即调用函数ioctl的返回值等于-1,那么很可能是因为系统帧缓冲区在硬件上不支持双缓冲,因此,接下来的代码就会重新将显示屏的虚拟分辨率的高度值设置为可视分辨率的高度值,并且将变量flags的PAGE_FLIP位置为0。

        另一方面,如果调用函数ioctl成功,但是最终获得的显示屏的虚拟分辨率的高度值小于可视分辨率的高度值的2倍,那么也说明系统帧缓冲区在硬件上不支持双缓冲。在这种情况下,接下来的代码也会重新将显示屏的虚拟分辨率的高度值设置为可视分辨率的高度值,并且将变量flags的PAGE_FLIP位置为0。
        再继续往下看函数mapFrameBufferLocked:
  1. if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)  
  2.     return -errno;  
  3.   
  4. uint64_t  refreshQuotient =  
  5. (  
  6.         uint64_t( info.upper_margin + info.lower_margin + info.yres )  
  7.         * ( info.left_margin  + info.right_margin + info.xres )  
  8.         * info.pixclock  
  9. );  
  10.   
  11. /* Beware, info.pixclock might be 0 under emulation, so avoid a 
  12.  * division-by-0 here (SIGFPE on ARM) */  
  13. int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;  
  14.   
  15. if (refreshRate == 0) {  
  16.     // bleagh, bad info from the driver  
  17.     refreshRate = 60*1000;  // 60 Hz  
  18. }  

        这段代码再次通过IO控制命令FBIOGET_VSCREENINFO来获得系统帧缓冲区的可变属性信息,并且保存在fb_var_screeninfo结构体info中,接下来再计算设备显示屏的刷新频率。




本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967078,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
202 4
|
1月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
32 8
|
2月前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
43 1
|
2月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
2月前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
30 1
|
30天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
53 19
|
2月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
30天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
59 14
|
1月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。