https://blog.csdn.net/yangwen123/article/details/12192401html
FrameBuffer驅動程序分析文中介紹了Linux系統下的顯示驅動框架,每一個顯示屏被抽象爲一個幀緩衝區,註冊到FrameBuffer模塊中,並在/dev/graphics目錄下建立對應的fbX設備。Android系統在硬件抽象層中提供了一個Gralloc模塊,封裝了對幀緩衝區的全部訪問操做。用戶空間的應用程序在使用幀緩衝區之間,首先要加載Gralloc模塊,而且得到一個gralloc設備和一個fb設備。有了gralloc設備以後,用戶空間中的應用程序就能夠申請分配一塊圖形緩衝區,而且將這塊圖形緩衝區映射到應用程序的地址空間來,以即可以向裏面寫入要繪製的畫面的內容。最後,用戶空間中的應用程序就經過fb設備來將已經準備好了的圖形緩衝區渲染到幀緩衝區中去,即將圖形緩衝區的內容繪製到顯示屏中去。相應地,當用戶空間中的應用程序再也不須要使用一塊圖形緩衝區的時候,就能夠經過gralloc設備來釋放它,而且將它從地址空間中解除映射。數據結構
Gralloc模塊實現源碼位於:hardware/libhardware/modules/grallocapp
├── Android.mk
├── framebuffer.cpp
├── gralloc.cpp
├── gralloc_priv.h
├── gr.h
└── mapper.cpp框架
Android硬件抽象Hardware庫加載過程源碼分析介紹了Android系統中的硬件抽象層模塊的加載過程,並指出每一個硬件抽象層模塊都必須定義HAL_MODULE_INFO_SYM符號,而且有本身惟一的ID,Gralloc也不例外,Gralloc模塊ID定義爲:ide
1 #define GRALLOC_HARDWARE_MODULE_ID "gralloc"
同時定義了以HAL_MODULE_INFO_SYM爲符號的類型爲private_module_t的結構體:函數
hardware\libhardware\modules\gralloc\gralloc.cpp源碼分析
1 static struct hw_module_methods_t gralloc_module_methods = { 2 open: gralloc_device_open 3 }; 4 struct private_module_t HAL_MODULE_INFO_SYM = { 5 base: { 6 common: { 7 tag: HARDWARE_MODULE_TAG, 8 version_major: 1, 9 version_minor: 0, 10 id: GRALLOC_HARDWARE_MODULE_ID, 11 name: "Graphics Memory Allocator Module", 12 author: "The Android Open Source Project", 13 methods: &gralloc_module_methods 14 }, 15 registerBuffer: gralloc_register_buffer, 16 unregisterBuffer: gralloc_unregister_buffer, 17 lock: gralloc_lock, 18 unlock: gralloc_unlock, 19 }, 20 framebuffer: 0, 21 flags: 0, 22 numBuffers: 0, 23 bufferMask: 0, 24 lock: PTHREAD_MUTEX_INITIALIZER, 25 currentBuffer: 0, 26 };
經過Android硬件抽象Hardware庫加載過程源碼分析的方法將Gralloc模塊加載到內存中來以後,就能夠調用函數dlsym來得到它所導出的符號HMI,獲得private_module_t的首地址後,因爲private_module_t的第一個成員變量的類型爲gralloc_module_t,所以也是gralloc_module_t的首地址,因爲gralloc_module_t的第一個成員變量類型爲hw_module_t,所以也是hw_module_t的首地址,所以只要獲得這三種類型中其中一種類型變量的地址,就能夠相互轉換爲其餘兩種類型的指針。post
在分析Gralloc模塊以前,首先介紹Gralloc模塊定義的一些數據結構。private_module_t用於描述Gralloc模塊下的系統幀緩衝區信息ui
1 struct private_module_t { 2 gralloc_module_t base; 3 private_handle_t* framebuffer; //指向系統幀緩衝區的句柄 4 uint32_t flags; //用來標誌系統幀緩衝區是否支持雙緩衝 5 uint32_t numBuffers;//表示系統幀緩衝區包含有多少個圖形緩衝區 6 uint32_t bufferMask; //記錄系統幀緩衝區中的圖形緩衝區的使用狀況 7 pthread_mutex_t lock; //一個互斥鎖,用來保護結構體private_module_t的並行訪問 8 buffer_handle_t currentBuffer; //用來描述當前正在被渲染的圖形緩衝區 9 int pmem_master; 10 void* pmem_master_base; 11 struct fb_var_screeninfo info; //保存設備顯示屏的動態屬性信息 12 struct fb_fix_screeninfo finfo; ////保存設備顯示屏的固定屬性信息 13 float xdpi; //描述設備顯示屏在寬度 14 float ydpi; //描述設備顯示屏在高度 15 float fps; //用來描述顯示屏的刷新頻率 16 };
1 typedef struct framebuffer_device_t { 2 struct hw_device_t common; 3 const uint32_t flags;//用來記錄系統幀緩衝區的標誌 4 const uint32_t width;//用來描述設備顯示屏的寬度 5 const uint32_t height;//用來描述設備顯示屏的高度 6 const int stride;//用來描述設備顯示屏的一行有多少個像素點 7 const int format;//用來描述系統幀緩衝區的像素格式 8 const float xdpi;//用來描述設備顯示屏在寬度上的密度 9 const float ydpi;//用來描述設備顯示屏在高度上的密度 10 const float fps;//用來描述設備顯示屏的刷新頻率 11 const int minSwapInterval;//用來描述幀緩衝區交換先後兩個圖形緩衝區的最小時間間隔 12 const int maxSwapInterval;//用來描述幀緩衝區交換先後兩個圖形緩衝區的最大時間間隔 13 int reserved[8];//保留 14 //用來設置幀緩衝區交換先後兩個圖形緩衝區的最小和最大時間間隔 15 int (*setSwapInterval)(struct framebuffer_device_t* window,int interval); 16 //用來設置幀緩衝區的更新區域 17 int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height); 18 //用來將圖形緩衝區buffer的內容渲染到幀緩衝區中去 19 int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer); 20 //用來通知fb設備,圖形緩衝區的組合工做已經完成 21 int (*compositionComplete)(struct framebuffer_device_t* dev); 22 void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len); 23 int (*enableScreen)(struct framebuffer_device_t* dev, int enable); 24 //保留 25 void* reserved_proc[6]; 26 } framebuffer_device_t
gralloc_module_t用於描述gralloc模塊信息spa
1 typedef struct gralloc_module_t { 2 struct hw_module_t common; 3 //映射一塊圖形緩衝區到一個進程的地址空間去 4 int (*registerBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle); 5 //取消映射一塊圖形緩衝區到一個進程的地址空間去 6 int (*unregisterBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle); 7 //鎖定一個指定的圖形緩衝區 8 int (*lock)(struct gralloc_module_t const* module,buffer_handle_t handle, int usage, 9 int l, int t, int w, int h,void** vaddr); 10 //解鎖一個指定的圖形緩衝區 11 int (*unlock)(struct gralloc_module_t const* module,buffer_handle_t handle); 12 int (*perform)(struct gralloc_module_t const* module,int operation, ... ); 13 void* reserved_proc[7]; 14 } gralloc_module_t;
alloc_device_t用於描述gralloc設備的信息
1 typedef struct alloc_device_t { 2 struct hw_device_t common; 3 //用於分配一塊圖形緩衝區 4 int (*alloc)(struct alloc_device_t* dev,int w, int h, int format, int usage,buffer_handle_t* handle, int* stride); 5 //用於釋放指定的圖形緩衝區 6 int (*free)(struct alloc_device_t* dev,buffer_handle_t handle); 7 void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len); 8 void* reserved_proc[7]; 9 } alloc_device_t;
1 typedef struct hw_module_t { 2 uint32_t tag;//標籤 3 uint16_t version_major;//模塊主設備號 4 uint16_t version_minor;//模塊次設備號 5 const char *id;//模塊ID 6 const char *name;//模塊名稱 7 const char *author;//模塊做者 8 struct hw_module_methods_t* methods;//模塊操做方法 9 void* dso;//保存模塊首地址 10 uint32_t reserved[32-7];//保留位 11 } hw_module_t;
硬件抽象層Gralloc模塊定義了設備fb和設備gpu:
1 #define GRALLOC_HARDWARE_FB0 "fb0" 2 #define GRALLOC_HARDWARE_GPU0 "gpu0"
設備gpu用於分配圖形緩衝區,而設備fb用於渲染圖形緩衝區;hw_module_t用於描述硬件抽象層Gralloc模塊,而hw_device_t則用於描述硬件抽象層Gralloc設備,經過硬件抽象層設備能夠找到對應的硬件抽象層模塊。在Gralloc模塊中,不管是定義fb設備仍是gpu設備,都是用來處理圖形緩衝區,如下是關於緩衝區的數據結構 定義:
private_handle_t用來描述一塊緩衝區,Android對緩衝區的定義提供了C和C++兩種方式,C語言編譯器下的定義:
1 struct private_handle_t { 2 struct native_handle nativeHandle; 3 enum { 4 PRIV_FLAGS_FRAMEBUFFER = 0x00000001 5 }; 6 int fd; //指向一個文件描述符,這個文件描述符要麼指向幀緩衝區設備,要麼指向一塊匿名共享內存 7 int magic; 8 int flags;//用來描述一個緩衝區的標誌,當一個緩衝區的標誌值等於PRIV_FLAGS_FRAMEBUFFER的時候,就表示它是在幀緩衝區中分配的。 9 int size;//用來描述一個緩衝區的大小 10 int offset;//用來描述一個緩衝區的偏移地址 11 int base;//用來描述一個緩衝區的實際地址 12 int pid;//用來描述一個緩衝區的建立者的PID 13 };
C++編譯器下的定義:
1 struct private_handle_t : public native_handle { 2 enum { 3 PRIV_FLAGS_FRAMEBUFFER = 0x00000001 4 }; 5 int fd; //指向一個文件描述符,這個文件描述符要麼指向幀緩衝區設備,要麼指向一塊匿名共享內存 6 int magic;//指向一個魔數,它的值由靜態成員變量sMagic來指定,用來標識一個private_handle_t結構體。 7 int flags;//用來描述一個緩衝區的標誌,它的值要麼等於0,要麼等於PRIV_FLAGS_FRAMEBUFFER 8 int size;//用來描述一個緩衝區的大小。 9 int offset;//用來描述一個緩衝區的偏移地址。 10 int base;//用來描述一個緩衝區的實際地址,它是經過成員變量offset來計算獲得的。 11 int pid;//用來描述一個緩衝區的建立者的PID。 12 static const int sNumInts = 6; //包含有6個整數 13 static const int sNumFds = 1; //包含有1個文件描述符 14 static const int sMagic = 0x3141592; 15 };
1 typedef struct native_handle 2 { 3 int version; //設置爲結構體native_handle_t的大小,用來標識結構體native_handle_t的版本 4 int numFds; //表示結構體native_handle_t所包含的文件描述符的個數,這些文件描述符保存在成員變量data所指向的一塊緩衝區中。 5 int numInts; //表示結構體native_handle_t所包含的整數值的個數,這些整數保存在成員變量data所指向的一塊緩衝區中。 6 int data[0]; //指向的一塊緩衝區中 7 } native_handle_t; 8 typedef const native_handle_t* buffer_handle_t;
fb設備的ID值定義爲#defineGRALLOC_HARDWARE_FB0"fb0",fb設備使用結構體framebuffer_device_t來描述。結構體framebuffer_device_t是用來描述系統幀緩衝區的信息
hardware\libhardware\include\hardware\fb.h
1 framebuffer_open(const struct hw_module_t* module, 2 struct framebuffer_device_t** device) { 3 return module->methods->open(module,GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device); 4 }
hardware\libhardware\modules\gralloc\gralloc.cpp
gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device) { int status = -EINVAL; if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { ... } else { status = fb_device_open(module, name, device); } return status; }
hardware\libhardware\modules\gralloc\framebuffer.cpp
1 int fb_device_open(hw_module_t const* module, const char* name, 2 hw_device_t** device) 3 { 4 int status = -EINVAL; 5 //判斷打開的是fb設備 6 if (!strcmp(name, GRALLOC_HARDWARE_FB0)) { 7 alloc_device_t* gralloc_device; 8 //打開gpu設備 9 status = gralloc_open(module, &gralloc_device); 10 if (status < 0) 11 return status; 12 //建立一個fb_context_t對象,用來描述fb設備上下文 13 fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev)); 14 memset(dev, 0, sizeof(*dev)); 15 //初始化fb_context_t對象 16 dev->device.common.tag = HARDWARE_DEVICE_TAG; 17 dev->device.common.version = 0; 18 dev->device.common.module = const_cast<hw_module_t*>(module); 19 //註冊fb設備的操做函數 20 dev->device.common.close = fb_close; 21 dev->device.setSwapInterval = fb_setSwapInterval; 22 dev->device.post = fb_post; 23 dev->device.setUpdateRect = 0; 24 25 private_module_t* m = (private_module_t*)module; 26 //將fb映射到當前進程地址空間 27 status = mapFrameBuffer(m); 28 if (status >= 0) { 29 int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3); 30 int format = (m->info.bits_per_pixel == 32) 31 ? HAL_PIXEL_FORMAT_RGBX_8888 32 : HAL_PIXEL_FORMAT_RGB_565; 33 const_cast<uint32_t&>(dev->device.flags) = 0; 34 const_cast<uint32_t&>(dev->device.width) = m->info.xres; 35 const_cast<uint32_t&>(dev->device.height) = m->info.yres; 36 const_cast<int&>(dev->device.stride) = stride; 37 const_cast<int&>(dev->device.format) = format; 38 const_cast<float&>(dev->device.xdpi) = m->xdpi; 39 const_cast<float&>(dev->device.ydpi) = m->ydpi; 40 const_cast<float&>(dev->device.fps) = m->fps; 41 const_cast<int&>(dev->device.minSwapInterval) = 1; 42 const_cast<int&>(dev->device.maxSwapInterval) = 1; 43 *device = &dev->device.common; 44 } 45 } 46 return status; 47 }
這個函數主要是用來建立一個fb_context_t結構體,而且對它的成員變量device進行初始化。結構體fb_context_t的成員變量device的類型爲framebuffer_device_t,它是用來描述fb設備的。fb設備主要是用來渲染圖形緩衝區的,這是經過調用它的成員函數post來實現的。函數fb_device_open所打開的fb設備的成員函數post被設置爲Gralloc模塊中的函數fb_post。函數mapFrameBuffer除了用來得到系統幀緩衝區的信息以外,還會將系統幀緩衝區映射到當前進程的地址空間來。line_length用來描述顯示屏一行像素總共所佔用的字節數,bits_per_pixel用來描述顯示屏每個像素所佔用的位數,bits_per_pixel的值向右移3位,就能夠獲得顯示屏每個像素所佔用的字節數。用顯示屏像素總共所佔用的字節數line_length除以每個像素所佔用的字節數就能夠獲得顯示屏一行有多少個像素點,並保存在stride中。
1 stride = line_length / ( bits_per_pixel >> 3)
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函數執行映射過程,該函數在線程保護下完成。
1 int mapFrameBufferLocked(struct private_module_t* module) 2 { 3 // already initialized... 4 if (module->framebuffer) { 5 return 0; 6 } 7 char const * const device_template[] = { 8 "/dev/graphics/fb%u", 9 "/dev/fb%u", 10 0 }; 11 12 int fd = -1; 13 int i=0; 14 char name[64]; 15 //檢查是否存在設備文件/dev/graphics/fb0或者/dev/fb0。若是存在的話,那麼就調用函數open來打開它,而且將獲得的文件描述符保存在變量fd中 16 while ((fd==-1) && device_template[i]) { 17 snprintf(name, 64, device_template[i], 0); 18 fd = open(name, O_RDWR, 0); 19 i++; 20 } 21 if (fd < 0) 22 return -errno; 23 //經過IO控制命令FBIOGET_FSCREENINFO來得到系統幀緩衝區的固定信息,保存在fb_fix_screeninfo結構體finfo中 24 struct fb_fix_screeninfo finfo; 25 if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) 26 return -errno; 27 //經過IO控制命令FBIOGET_VSCREENINFO來得到系統幀緩衝區的可變信息,保存在fb_var_screeninfo結構體info中 28 struct fb_var_screeninfo info; 29 if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) 30 return -errno; 31 //初始化info 32 info.reserved[0] = 0; 33 info.reserved[1] = 0; 34 info.reserved[2] = 0; 35 info.xoffset = 0; 36 info.yoffset = 0; 37 info.activate = FB_ACTIVATE_NOW; 38 //fb_var_screeninfo的成員變量xres和yres用來描述顯示屏的可視分辨率,而成員變量xres_virtual和yres_virtual用來描述顯示屏的虛擬分辨率。 39 //將虛擬分辨率的高度值設置爲可視分辨率的高度值的NUM_BUFFERS倍。 40 info.yres_virtual = info.yres * NUM_BUFFERS; //2 41 uint32_t flags = PAGE_FLIP; 42 //設置設備顯示屏的虛擬分辨率 43 if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { 44 //設置失敗,從新設置顯示屏的虛擬分辨率 45 info.yres_virtual = info.yres; 46 flags &= ~PAGE_FLIP; 47 ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); 48 } 49 if (info.yres_virtual < info.yres * 2) { 50 // we need at least 2 for page-flipping 51 info.yres_virtual = info.yres; 52 flags &= ~PAGE_FLIP; 53 ALOGW("page flipping not supported (yres_virtual=%d, requested=%d)",info.yres_virtual, info.yres*2); 54 } 55 //經過IO控制命令FBIOGET_VSCREENINFO來從新得到系統幀緩衝區的可變信息 56 if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) 57 return -errno; 58 //計算設備顯示屏的刷新頻率 59 uint64_t refreshQuotient = 60 ( 61 uint64_t( info.upper_margin + info.lower_margin + info.yres )* ( info.left_margin + info.right_margin + info.xres )* info.pixclock 62 ); 63 //模擬器的info.pixclock=0,所以計算獲得的refreshQuotient=0 64 int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0; 65 //若是是模擬器,設置刷新頻率爲60 Hz 66 if (refreshRate == 0) { 67 refreshRate = 60*1000; // 60 Hz 68 } 69 if (int(info.width) <= 0 || int(info.height) <= 0) { 70 info.width = ((info.xres * 25.4f)/160.0f + 0.5f); 71 info.height = ((info.yres * 25.4f)/160.0f + 0.5f); 72 } 73 //計算顯示屏的密度 74 float xdpi = (info.xres * 25.4f) / info.width; 75 float ydpi = (info.yres * 25.4f) / info.height; 76 float fps = refreshRate / 1000.0f; 77 //再次經過IO控制命令FBIOGET_FSCREENINFO來得到系統幀緩衝區的固定信息 78 if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) 79 return -errno; 80 if (finfo.smem_len <= 0) 81 return -errno; 82 //獲得的系統幀緩衝區的其它信息來初始化參數module所描述的一個private_module_t結構體。 83 module->flags = flags; 84 module->info = info; 85 module->finfo = finfo; 86 module->xdpi = xdpi; 87 module->ydpi = ydpi; 88 module->fps = fps; 89 int err; 90 //整個系統幀緩衝區的大小=虛擬分辨率的高度值info.yres_virtual * 每一行所佔用的字節數finfo.line_length,並將整個系統幀緩衝區的大小對齊到頁面邊界 91 size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual); 92 //建立一個private_handle_t,用來描述整個系統幀緩衝區的信息, 93 module->framebuffer = new private_handle_t(dup(fd), fbSize, 0); 94 //計算整個系統幀緩衝區能夠劃分爲多少個圖形緩衝區來使用 95 module->numBuffers = info.yres_virtual / info.yres; 96 //表示系統幀緩衝區中的全部圖形緩衝區都是處於空閒狀態 97 module->bufferMask = 0; 98 //以讀寫共享方式將幀緩衝區映射到當前進程地址空間中 99 void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 100 if (vaddr == MAP_FAILED) { 101 ALOGE("Error mapping the framebuffer (%s)", strerror(errno)); 102 return -errno; 103 } 104 //系統幀緩衝區在當前進程的地址空間中的起始地址保存到private_handle_t的域base中 105 module->framebuffer->base = intptr_t(vaddr); 106 //清空大小爲fbSize的幀緩衝區 107 memset(vaddr, 0, fbSize); 108 return 0; 109 }
在瞭解本節內容以前首先須要瞭解Linux的FrameBuffer驅動,請查看FrameBuffer驅動程序分析.
1 #defineGRALLOC_HARDWARE_GPU0"gpu0"
hardware\libhardware\include\hardware\gralloc.h
1 static inline int gralloc_open(const struct hw_module_t* module, 2 struct alloc_device_t** device) { 3 return module->methods->open(module, 4 GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device); 5 }
module指向的是一個用來描述Gralloc模塊的hw_module_t結構體,它的成員變量methods所指向的一個hw_module_methods_t結構體的成員函數open指向了Gralloc模塊中的函數gralloc_device_open。前面介紹了函數gralloc_device_open便可以打開fb設備也可用來打開gpu設備,這裏傳入的設備名爲GRALLOC_HARDWARE_GPU0,表示當前打開的是gpu設備。
hardware\libhardware\modules\gralloc\gralloc.cpp
1 int gralloc_device_open(const hw_module_t* module, const char* name, 2 hw_device_t** device) 3 { 4 int status = -EINVAL; 5 if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { 6 gralloc_context_t *dev; 7 dev = (gralloc_context_t*)malloc(sizeof(*dev)); 8 /* initialize our state here */ 9 memset(dev, 0, sizeof(*dev)); 10 /* initialize the procs */ 11 dev->device.common.tag = HARDWARE_DEVICE_TAG; 12 dev->device.common.version = 0; 13 dev->device.common.module = const_cast<hw_module_t*>(module); 14 dev->device.common.close = gralloc_close; 15 dev->device.alloc = gralloc_alloc; 16 dev->device.free = gralloc_free; 17 *device = &dev->device.common; 18 status = 0; 19 } else { 20 ... 21 } 22 return status; 23 }
這個函數主要是用來建立一個gralloc_context_t結構體,而且對它的成員變量device進行初始化。結構體gralloc_context_t的成員變量device的類型爲gralloc_device_t,它用來描述一個gralloc設備。前面提到,gralloc設備是用來分配和釋放圖形緩衝區的,這是經過調用它的成員函數alloc和free來實現的。從這裏能夠看出,函數gralloc_device_open所打開的gralloc設備的成員函數alloc和free分別被設置爲Gralloc模塊中的函數gralloc_alloc和gralloc_free。