【android開發】Android GUI系統學習1:Gralloc
Gralloc模組是從Android Eclair(android 2.1)開始加入的一個HAL模組,Gralloc的含義為是Graphics Alloc(圖形分配)。他對上為libui提供服務,為其分配視訊記憶體,重新整理顯示等。對下對framebuffer進行管理。
gralloc程式碼通常位於hardware/libhardware/modules/gralloc目錄下。包含以下幾個檔案:
Android.mk framebuffer.cpp gralloc.cpp gralloc_priv.h gr.h mapper.cpp
另外,與其相關的標頭檔案位於hardware/libhardware/include/hardware,涉及fb.h和gralloc.h。
下面從gralloc的呼叫開始學習gralloc的程式碼。程式碼基於android4.4。
gralloc的呼叫是從FramebufferNativeWindow.cpp的建構函式開始的。FramebufferNativeWindow實現FrameBuffer的管理,它主要被SurfaceFlinger使用,也可以被OpenGL Native程式使用。在本質上,它在Framebuffer之上實現了一個ANativeWindow,目前它只管理兩個buffers:front and back buffer。
如下所示(FramebufferNativeWindow.cpp):
FramebufferNativeWindow::FramebufferNativeWindow() : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false) { hw_module_t const* module; if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { int stride; int err; int i; err = framebuffer_open(module, &fbDev); ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err)); err = gralloc_open(module, &grDev); ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err)); // bail out if we can't initialize the modules if (!fbDev || !grDev) return; mUpdateOnDemand = (fbDev->setUpdateRect != 0); // initialize the buffer FIFO if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS && fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){ mNumBuffers = fbDev->numFramebuffers; } else { mNumBuffers = MIN_NUM_FRAME_BUFFERS; } mNumFreeBuffers = mNumBuffers; mBufferHead = mNumBuffers-1; /* * This does not actually change the framebuffer format. It merely * fakes this format to surfaceflinger so that when it creates * framebuffer surfaces it will use this format. It's really a giant * HACK to allow interworking with buggy gralloc+GPU driver * implementations. You should *NEVER* need to set this for shipping * devices. */ #ifdef FRAMEBUFFER_FORCE_FORMAT *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT; #endif for (i = 0; i < mNumBuffers; i++) { buffers[i] = new NativeBuffer( fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); } for (i = 0; i < mNumBuffers; i++) { err = grDev->alloc(grDev, fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride); ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s", i, fbDev->width, fbDev->height, strerror(-err)); if (err) { mNumBuffers = i; mNumFreeBuffers = i; mBufferHead = mNumBuffers-1; break; } } const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi; const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi; const_cast<int&>(ANativeWindow::minSwapInterval) = fbDev->minSwapInterval; const_cast<int&>(ANativeWindow::maxSwapInterval) = fbDev->maxSwapInterval; } else { ALOGE("Couldn't get gralloc module"); } ANativeWindow::setSwapInterval = setSwapInterval; ANativeWindow::dequeueBuffer = dequeueBuffer; ANativeWindow::queueBuffer = queueBuffer; ANativeWindow::query = query; ANativeWindow::perform = perform; ANativeWindow::dequeueBuffer_DEPRECATED = dequeueBuffer_DEPRECATED; ANativeWindow::lockBuffer_DEPRECATED = lockBuffer_DEPRECATED; ANativeWindow::queueBuffer_DEPRECATED = queueBuffer_DEPRECATED; }
這裡會先根據gralloc的module ID來得到hw_module_t結構。hw_get_module->hw_get_module_by_class。在hw_get_module_by_class裡面,首先根據平臺配置找到gralloc動態庫的位置,預設使用gralloc.default.so。 參見以下程式碼(hardware.c):
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) { if (i < HAL_VARIANT_KEYS_COUNT) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH2, name, prop); if (access(path, R_OK) == 0) break; snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH1, name, prop); if (access(path, R_OK) == 0) break; } else { snprintf(path, sizeof(path), "%s/%s.default.so", HAL_LIBRARY_PATH2, name); if (access(path, R_OK) == 0) break; snprintf(path, sizeof(path), "%s/%s.default.so", HAL_LIBRARY_PATH1, name); if (access(path, R_OK) == 0) break; } } status = -ENOENT; if (i < HAL_VARIANT_KEYS_COUNT+1) { /* load the module, if this fails, we're doomed, and we should not try * to load a different variant. */ status = load(class_id, path, module); }
找到gralloc庫的路徑後,會呼叫load函式,在load函式中使用dlopen開啟找到的庫,並根據HAL_MODULE_INFO_SYM_AS_STR(其值為HMI)獲取到hw_module_t(即HAL_MODULE_INFO_SYM)結構體指標,以及把dlopen返回的handle儲存在hw_module_t中。而hw_module_t HMI 結構是一個全域性結構,在gralloc.cpp中已經得到初始化了。這也是為什麼每一個HAL模組都要定義並初始化一個名字為HAL_MODULE_INFO_SYM的hw_module_t結構
struct private_module_t HAL_MODULE_INFO_SYM = {
base: {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: GRALLOC_HARDWARE_MODULE_ID,
name: "Graphics Memory Allocator Module",
author: "The Android Open Source Project",
methods: &gralloc_module_methods
},
registerBuffer: gralloc_register_buffer,
unregisterBuffer: gralloc_unregister_buffer,
lock: gralloc_lock,
unlock: gralloc_unlock,
},
framebuffer: 0,
flags: 0,
numBuffers: 0,
bufferMask: 0,
lock: PTHREAD_MUTEX_INITIALIZER,
currentBuffer: 0,
};
回過頭,回到FramebufferNativeWindow的構造函數出,接下來呼叫了err = framebuffer_open(module, &fbDev);framebuffer_open定義在fb.h中,是一個inline函式,其實最終呼叫了就是上面結構體中初始化的open函式,open函式指向gralloc_device_open,其實現為(gralloc.cpp):
int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
gralloc_context_t *dev;
dev = (gralloc_context_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free = gralloc_free;
*device = &dev->device.common;
status = 0;
} else {
status = fb_device_open(module, name, device);
}
return status;
}
fb_device_open的定義如下所示(framebuffer.cpp):
int fb_device_open(hw_module_t const* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
/* initialize our state here */
fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0;
private_module_t* m = (private_module_t*)module;
status = mapFrameBuffer(m);
if (status >= 0) {
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
int format = (m->info.bits_per_pixel == 32)
? HAL_PIXEL_FORMAT_RGBX_8888
: HAL_PIXEL_FORMAT_RGB_565;
const_cast<uint32_t&>(dev->device.flags) = 0;
const_cast<uint32_t&>(dev->device.width) = m->info.xres;
const_cast<uint32_t&>(dev->device.height) = m->info.yres;
const_cast<int&>(dev->device.stride) = stride;
const_cast<int&>(dev->device.format) = format;
const_cast<float&>(dev->device.xdpi) = m->xdpi;
const_cast<float&>(dev->device.ydpi) = m->ydpi;
const_cast<float&>(dev->device.fps) = m->fps;
const_cast<int&>(dev->device.minSwapInterval) = 1;
const_cast<int&>(dev->device.maxSwapInterval) = 1;
*device = &dev->device.common;
}
}
return status;
}
接下來的gralloc_open也是呼叫了gralloc_device_open,只不過name引數一個是GRALLOC_HARDWARE_GPU0,而另外一個是GRALLOC_HARDWARE_FB0,這兩個函式分別得到alloc_device_t 和 framebuffer_device_t結構。到現在為止,gralloc模組的三個主要結構體,gralloc_module_t,alloc_device_t,framebuffer_device_t都已經獲取到了。其中在fb_device_open函式中會獲取實際的framebuffer裝置(通常是/dev/graphics/fb0)的一些重要引數以及能力,比如解析度資訊以及支援多少個緩衝等,另外會把framebuffer對映到內測的地址儲存到alloc_module_t中。android一般使用的都是雙緩衝機制。具體程式碼如下(framebuffer.cpp),其中涉及到對private_module_t中一些成員的完善,涉及到gralloc_module_t以及private_handle_t等,其定義在gralloc_priv.h中,這兩個結構中都儲存了framebuffer的一些私有資訊。
int mapFrameBufferLocked(struct private_module_t* module)
{
// already initialized...
if (module->framebuffer) {
return 0;
}
char const * const device_template[] = {
"/dev/graphics/fb%u",
"/dev/fb%u",
0 };
int fd = -1;
int i=0;
char name[64];
while ((fd==-1) && device_template[i]) {
snprintf(name, 64, device_template[i], 0);
fd = open(name, O_RDWR, 0);
i++;
}
if (fd < 0)
return -errno;
struct fb_fix_screeninfo finfo;
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
return -errno;
struct fb_var_screeninfo info;
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
return -errno;
info.reserved[0] = 0;
info.reserved[1] = 0;
info.reserved[2] = 0;
info.xoffset = 0;
info.yoffset = 0;
info.activate = FB_ACTIVATE_NOW;
/*
* Request NUM_BUFFERS screens (at lest 2 for page flipping)
*/
info.yres_virtual = info.yres * NUM_BUFFERS;
uint32_t flags = PAGE_FLIP;
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
}
if (info.yres_virtual < info.yres * 2) {
// we need at least 2 for page-flipping
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
ALOGW("page flipping not supported (yres_virtual=%d, requested=%d)",
info.yres_virtual, info.yres*2);
}
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
return -errno;
uint64_t refreshQuotient =
(
uint64_t( info.upper_margin + info.lower_margin + info.yres )
* ( info.left_margin + info.right_margin + info.xres )
* info.pixclock
);
/* Beware, info.pixclock might be 0 under emulation, so avoid a
* division-by-0 here (SIGFPE on ARM) */
int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;
if (refreshRate == 0) {
// bleagh, bad info from the driver
refreshRate = 60*1000; // 60 Hz
}
if (int(info.width) <= 0 || int(info.height) <= 0) {
// the driver doesn't return that information
// default to 160 dpi
info.width = ((info.xres * 25.4f)/160.0f + 0.5f);
info.height = ((info.yres * 25.4f)/160.0f + 0.5f);
}
float xdpi = (info.xres * 25.4f) / info.width;
float ydpi = (info.yres * 25.4f) / info.height;
float fps = refreshRate / 1000.0f;
ALOGI( "using (fd=%d)n"
"id = %sn"
"xres = %d pxn"
"yres = %d pxn"
"xres_virtual = %d pxn"
"yres_virtual = %d pxn"
"bpp = %dn"
"r = %2u:%un"
"g = %2u:%un"
"b = %2u:%un",
fd,
finfo.id,
info.xres,
info.yres,
info.xres_virtual,
info.yres_virtual,
info.bits_per_pixel,
info.red.offset, info.red.length,
info.green.offset, info.green.length,
info.blue.offset, info.blue.length
);
ALOGI( "width = %d mm (%f dpi)n"
"height = %d mm (%f dpi)n"
"refresh rate = %.2f Hzn",
info.width, xdpi,
info.height, ydpi,
fps
);
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
return -errno;
if (finfo.smem_len <= 0)
return -errno;
module->flags = flags;
module->info = info;
module->finfo = finfo;
module->xdpi = xdpi;
module->ydpi = ydpi;
module->fps = fps;
/*
* map the framebuffer
*/
int err;
size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);
module->framebuffer = new private_handle_t(dup(fd), fbSize, 0);
module->numBuffers = info.yres_virtual / info.yres;
module->bufferMask = 0;
void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (vaddr == MAP_FAILED) {
ALOGE("Error mapping the framebuffer (%s)", strerror(errno));
return -errno;
}
module->framebuffer->base = intptr_t(vaddr);
memset(vaddr, 0, fbSize);
return 0;
}
由上面函式看出,mapFrameBufferLocked主要做了下面幾件事情: 1. 開啟framebuffer裝置
2. 獲取 fb_fix_screeninfo and fb_var_screeninfo
3. refill fb_var_screeninfo
4. 判斷是否支援PAGE_FLIP
5. 計算重新整理率
6. 列印gralloc資訊
7. 填充private_module_t
8. mmap the framebuffer
由於篇幅限制,這裡暫時說道這裡,如要檢視後續內容請檢視原文:http://www.coderonline.net/android-gui%e7%b3%bb%e7%bb%9f%e5%ad%a6%e4%b9%a01%ef%bc%9agralloc.html