一、OpenGL ES與EGL
Android的GUI系統是基於OpenGL/EGL來實現的。
- 由於OpenGL是通用函數庫,在不同平台系統上需要被“本土化”——把它與具體平台的窗口系統建立起關聯,FramebufferNativeWindow是負責OpenGL ES在Android平台上本地化的中介之一。為OpenGL ES配置本地窗口的是EGL,EGL介於本地窗口系統和Rendering API(即OPenGL ES)之間的一層接口。
- 與OpenGL相關的模塊可分為:
(1)配置類。 幫助OpenGL ES完成配置的,包括EGL、DisplayHardware都歸為此類。
(2)依賴類。 OpenGL ES正常運行所依賴的“本地化”的東西,如FramebufferNativeWindow。
(3)使用類。 使用者也可能是配置者,如DisplayDevice既扮演了構建OpenGL ES環境的角色,同時也是它的用戶。
OpenGL開發查看Android:OpenGL ES開發入門篇(待寫)
二、終端顯示設備 - Gralloc與Framebuffer
Linux內核提供了統一的framebuffer顯示驅動。Framebuffer是內核系統提供的圖形硬件的抽象描述,稱為buffer是因為它也占用了系統存儲空間的一部分,是一塊包含屏幕顯示信息的緩沖區。Framebuffer借助於Linux文件系統向上層應用提供了統一而高效的操作接口,讓用戶空間運行的程序比較容易地適配多種顯示設備。
Android系統中,framebuffer提供的設備節點為/dev/graphics/fb或者/dev/fb,其中fb0表示第一個主顯示屏幕,必須存在,當前系統實現中只用到了一個顯示屏。顯示系統使用HAL層間接引用底層架構,從而操作幀緩沖區。而完成這一中介任務的就是Gralloc.下圖為Gralloc的模塊簡圖,描述了gpu0和fb0的主要API.
Android.mk framebuffer.cpp gralloc.cpp gralloc_priv.h gr.h mapper.cpp
與其相關的頭文件位於hardware/libhardware/include/hardware,涉及fb.h和gralloc.h。
以下從gralloc的調用開始分析代碼(Android8.0):
FramebufferNativeWindow實現FrameBuffer的管理,它主要被SurfaceFlinger使用。也能夠被OpenGL Native程序使用。在本質上,它在Framebuffer之上實現了一個ANativeWindow,眼下它僅僅管理兩個buffers:front and back buffer。
/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
HWComposer::HWComposer( const sp<SurfaceFlinger>& flinger, EventHandler& handler) : mFlinger(flinger), mFbDev(0), mHwc(0), mNumDisplays(1), mCBContext(new cb_context), mEventHandler(handler), mDebugForceFakeVSync(false) { for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) { mLists[i] = 0; } for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) { mLastHwVSync[i] = 0; mVSyncCounts[i] = 0; } char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.no_hw_vsync", value, "0"); mDebugForceFakeVSync = atoi(value); bool needVSyncThread = true; // Note: some devices may insist that the FB HAL be opened before HWC. int fberr = loadFbHalModule(); loadHwcModule(); if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { // close FB HAL if we don't needed it. // FIXME: this is temporary until we're not forced to open FB HAL // before HWC. framebuffer_close(mFbDev); mFbDev = NULL; } // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory. if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) && !mFbDev) { ALOGE("ERROR: failed to open framebuffer (%s), aborting", strerror(-fberr)); abort(); } // these display IDs are always reserved for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { mAllocatedDisplayIDs.markBit(i); } if (mHwc) { ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER, (hwcApiVersion(mHwc) >> 24) & 0xff, (hwcApiVersion(mHwc) >> 16) & 0xff); if (mHwc->registerProcs) { mCBContext->hwc = this; mCBContext->procs.invalidate = &hook_invalidate; mCBContext->procs.vsync = &hook_vsync; if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) mCBContext->procs.hotplug = &hook_hotplug; else mCBContext->procs.hotplug = NULL; memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero)); mHwc->registerProcs(mHwc, &mCBContext->procs); } // don't need a vsync thread if we have a hardware composer needVSyncThread = false; // always turn vsync off when we start eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0); // the number of displays we actually have depends on the // hw composer version if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { // 1.3 adds support for virtual displays mNumDisplays = MAX_HWC_DISPLAYS; } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { // 1.1 adds support for multiple displays mNumDisplays = NUM_BUILTIN_DISPLAYS; } else { mNumDisplays = 1; } } if (mFbDev) { ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)), "should only have fbdev if no hwc or hwc is 1.0"); DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]); disp.connected = true; disp.format = mFbDev->format; DisplayConfig config = DisplayConfig(); config.width = mFbDev->width; config.height = mFbDev->height; config.xdpi = mFbDev->xdpi; config.ydpi = mFbDev->ydpi; config.refresh = nsecs_t(1e9 / mFbDev->fps); disp.configs.push_back(config); disp.currentConfig = 0; } else if (mHwc) { // here we're guaranteed to have at least HWC 1.1 for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { queryDisplayProperties(i); } } if (needVSyncThread) { // we don't have VSYNC support, we need to fake it mVSyncThread = new VSyncThread(*this); } }
先依據gralloc的module ID來得到hw_module_t結構:
// Load and prepare the FB HAL, which uses the gralloc module. Sets mFbDev. int HWComposer::loadFbHalModule() { hw_module_t const* module; int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); if (err != 0) { ALOGE("%s module not found", GRALLOC_HARDWARE_MODULE_ID); return err; } return framebuffer_open(module, &mFbDev); }
在hw_get_module里面調用hw_get_module_by_class,其中hw_module_exists會檢測gralloc動態庫的位置:
int hw_get_module_by_class(const char *class_id, const char *inst, const struct hw_module_t **module) { int i = 0; char prop[PATH_MAX] = {0}; char path[PATH_MAX] = {0}; char name[PATH_MAX] = {0}; char prop_name[PATH_MAX] = {0}; if (inst) snprintf(name, PATH_MAX, "%s.%s", class_id, inst); else strlcpy(name, class_id, PATH_MAX); /* * Here we rely on the fact that calling dlopen multiple times on * the same .so will simply increment a refcount (and not load * a new copy of the library). * We also assume that dlopen() is thread-safe. */ /* First try a property specific to the class and possibly instance */ snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name); if (property_get(prop_name, prop, NULL) > 0) { if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found; } } /* Loop through the configuration variants looking for a module */ for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found; } } /* Nothing found, try the default */ if (hw_module_exists(path, sizeof(path), name, "default") == 0) { goto found; } return -ENOENT; found: /* load the module, if this fails, we're doomed, and we should not try * to load a different variant. */ return load(class_id, path, module); }
找到gralloc庫的路徑后,會調用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 結構是一個全局結構。
/** * Load the file defined by the variant and if successful * return the dlopen handle and the hmi. * @return 0 = success, !0 = failure. */ static int load(const char *id, const char *path, const struct hw_module_t **pHmi) { int status = -EINVAL; void *handle = NULL; struct hw_module_t *hmi = NULL; /* * load the symbols resolving undefined symbols before * dlopen returns. Since RTLD_GLOBAL is not or'd in with * RTLD_NOW the external symbols will not be global */ if (strncmp(path, "/system/", 8) == 0) { /* If the library is in system partition, no need to check * sphal namespace. Open it with dlopen. */ handle = dlopen(path, RTLD_NOW); } else { handle = android_load_sphal_library(path, RTLD_NOW); } if (handle == NULL) { char const *err_str = dlerror(); ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown"); status = -EINVAL; goto done; } /* Get the address of the struct hal_module_info. */ const char *sym = HAL_MODULE_INFO_SYM_AS_STR; hmi = (struct hw_module_t *)dlsym(handle, sym); if (hmi == NULL) { ALOGE("load: couldn't find symbol %s", sym); status = -EINVAL; goto done; } /* Check that the id matches */ if (strcmp(id, hmi->id) != 0) { ALOGE("load: id=%s != hmi->id=%s", id, hmi->id); status = -EINVAL; goto done; } hmi->dso = handle; /* success */ status = 0; done: if (status != 0) { hmi = NULL; if (handle != NULL) { dlclose(handle); handle = NULL; } } else { ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p", id, path, *pHmi, handle); } *pHmi = hmi; return status; }
回到HWComposer的構造函數處,接下來調用了loadFbHalModule() ---> framebuffer_open(module, &mFbDev); framebuffer_open定義在fb.h中,是一個inline函數:
/** convenience API for opening and closing a supported device */ static inline int framebuffer_open(const struct hw_module_t* module, struct framebuffer_device_t** device) { return module->methods->open(module, GRALLOC_HARDWARE_FB0, TO_HW_DEVICE_T_OPEN(device)); }
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; }
gralloc_device_open函數既可以打開fb設備也可用來打開gpu設備,如果傳入的設備名為GRALLOC_HARDWARE_GPU0,即表示當前打開的是gpu設備。該函數主要是為了創建一個gralloc_context_t結構體,並且對它的成員變量device進行初始化,device變量的類型即為alloc_device_t,它用來描述一個gralloc設備,而gralloc設備是用來分配和釋放圖形緩沖區的,通過調用它成員函數alloc和free來實現。從這里可以看出,函數gralloc_device_open所打開的gralloc設備的成員函數alloc和free分別被設置為Gralloc模塊中的函數gralloc_alloc和gralloc_free。
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) ? (m->info.red.offset ? HAL_PIXEL_FORMAT_BGRA_8888 : 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)的一些重要參數以及能力,比如分辨率信息以及支持多少個緩沖等,另外會調用mapFrameBuffer--->mapFrameBufferLocked把framebuffer映射到內存的地址保存到gralloc_module_t中。android一般使用的都是雙緩沖機制。詳細代碼例如以下:
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 USE_PAN_DISPLAY if (ioctl(fd, FBIOPAN_DISPLAY, &info) == -1) { ALOGW("FBIOPAN_DISPLAY failed, page flipping not supported"); #else if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); #endif info.yres_virtual = info.yres; flags &= ~PAGE_FLIP; } 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 = %s\n" "xres = %d px\n" "yres = %d px\n" "xres_virtual = %d px\n" "yres_virtual = %d px\n" "bpp = %d\n" "r = %2u:%u\n" "g = %2u:%u\n" "b = %2u:%u\n", 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 Hz\n", 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主要做了以下幾件事情:
- 打開framebuffer設備
- 獲取 fb_fix_screeninfo and fb_var_screeninfo
- refill fb_var_screeninfo
- 推斷是否支持PAGE_FLIP
- 計算刷新率
- 打印gralloc信息
- 填充private_module_t
- mmap the framebuffer
typedef struct framebuffer_device_t { /** * Common methods of the framebuffer device. This *must* be the first member of * framebuffer_device_t as users of this structure will cast a hw_device_t to * framebuffer_device_t pointer in contexts where it's known the hw_device_t references a * framebuffer_device_t. */ struct hw_device_t common; /* flags describing some attributes of the framebuffer */ const uint32_t flags; /* dimensions of the framebuffer in pixels */ const uint32_t width; const uint32_t height; /* frambuffer stride in pixels */ const int stride; /* framebuffer pixel format */ const int format; /* resolution of the framebuffer's display panel in pixel per inch*/ const float xdpi; const float ydpi; /* framebuffer's display panel refresh rate in frames per second */ const float fps; /* min swap interval supported by this framebuffer */ const int minSwapInterval; /* max swap interval supported by this framebuffer */ const int maxSwapInterval; /* Number of framebuffers supported*/ const int numFramebuffers; int reserved[7]; /* * requests a specific swap-interval (same definition than EGL) * * Returns 0 on success or -errno on error. */ int (*setSwapInterval)(struct framebuffer_device_t* window, int interval); /* * This hook is OPTIONAL. * * It is non NULL If the framebuffer driver supports "update-on-demand" * and the given rectangle is the area of the screen that gets * updated during (*post)(). * * This is useful on devices that are able to DMA only a portion of * the screen to the display panel, upon demand -- as opposed to * constantly refreshing the panel 60 times per second, for instance. * * Only the area defined by this rectangle is guaranteed to be valid, that * is, the driver is not allowed to post anything outside of this * rectangle. * * The rectangle evaluated during (*post)() and specifies which area * of the buffer passed in (*post)() shall to be posted. * * return -EINVAL if width or height <=0, or if left or top < 0 */ int (*setUpdateRect)(struct framebuffer_device_t* window, int left, int top, int width, int height); /* * Post <buffer> to the display (display it on the screen) * The buffer must have been allocated with the * GRALLOC_USAGE_HW_FB usage flag. * buffer must be the same width and height as the display and must NOT * be locked. * * The buffer is shown during the next VSYNC. * * If the same buffer is posted again (possibly after some other buffer), * post() will block until the the first post is completed. * * Internally, post() is expected to lock the buffer so that a * subsequent call to gralloc_module_t::(*lock)() with USAGE_RENDER or * USAGE_*_WRITE will block until it is safe; that is typically once this * buffer is shown and another buffer has been posted. * * Returns 0 on success or -errno on error. */ int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer); /* * The (*compositionComplete)() method must be called after the * compositor has finished issuing GL commands for client buffers. */ int (*compositionComplete)(struct framebuffer_device_t* dev); /* * This hook is OPTIONAL. * * If non NULL it will be caused by SurfaceFlinger on dumpsys */ void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len); /* * (*enableScreen)() is used to either blank (enable=0) or * unblank (enable=1) the screen this framebuffer is attached to. * * Returns 0 on success or -errno on error. */ int (*enableScreen)(struct framebuffer_device_t* dev, int enable); void* reserved_proc[6]; } framebuffer_device_t; /** convenience API for opening and closing a supported device */ static inline int framebuffer_open(const struct hw_module_t* module, struct framebuffer_device_t** device) { return module->methods->open(module, GRALLOC_HARDWARE_FB0, TO_HW_DEVICE_T_OPEN(device)); } static inline int framebuffer_close(struct framebuffer_device_t* device) { return device->common.close(&device->common); } __END_DECLS #endif // ANDROID_FB_INTERFACE_H
另外,里面定義了一些操作framebuffer的函數:
1. int (*setSwapInterval)(struct framebuffer_device_t* window, int interval); // 這個函數基本沒實用,由於maxSwapInterval=minSwapInterval= 1。 2. int (*setUpdateRect)(struct framebuffer_device_t* window, int left, int top, int width, int height); // 局部刷新用的,默認沒有啟用,和平台有關。 3. int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer); // 這個是最關鍵的函數,用來將圖形緩沖區buffer的內容渲染到幀緩沖區中去,即顯示在設備的顯示屏中去。
具體看下post函數的實現:
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) { ALOGE("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; }
從fb_post的函數定義能夠看出,第一種方式是當private_handle_t結構體hnd所描寫敘述的圖形緩沖區是在系統幀緩沖區中分配的時候,即這個圖形緩沖區的標志值flags的PRIV_FLAGS_FRAMEBUFFER位等於1的時候,是不須要將圖形緩沖區的內容復制到系統幀緩沖區去的,由於將內容寫入到圖形緩沖區的時候,已經相當於是將內容寫入到了系統幀緩沖區中去了把Framebuffer的后buffer切為前buffer,然后通過IOCTRL機制告訴FB驅動切換DMA源地地址即可。設置系統幀緩沖區的當前輸出圖形緩沖區是通過IO控制命令FBIOPUT_VSCREENINFO來進行的,需要一個fb_var_screeninfo結構體作為參數。
另外一種方式是利用copy的方式來實現,當private_handle_t結構體hnd所描寫敘述的圖形緩沖區是在內存中分配時,即這個圖形緩沖區的標志值flags的PRIV_FLAGS_FRAMEBUFFER位等於0的時候,我們就須要將它的內容復制到系統幀緩沖區中去,這個拷貝的工作是通過調用函數memcpy來完成,相比效率較低。在將一塊內存緩沖區的內容復制到系統幀緩沖區中去之前,須要對這兩塊緩沖區進行鎖定,以保證在拷貝的過程中,這兩塊緩沖區的內容不會被改動,這個鎖定的工作是由Gralloc模塊中的函數gralloc_lock來實現的。Gralloc模塊中的函數gralloc_lock的地址正好就保存在private_module_t結構體m的成員變量base所描寫敘述的一個gralloc_module_t結構體的成員函數lock中。將要渲染的圖形緩沖區的內容復制到系統幀緩沖區之后,就能夠通過Gralloc模塊中的函數gralloc_unlock來解鎖。
以下再看看alloc_device_t的內容:
typedef struct alloc_device_t { struct hw_device_t common; /* * (*alloc)() Allocates a buffer in graphic memory with the requested * parameters and returns a buffer_handle_t and the stride in pixels to * allow the implementation to satisfy hardware constraints on the width * of a pixmap (eg: it may have to be multiple of 8 pixels). * The CALLER TAKES OWNERSHIP of the buffer_handle_t. * * If format is HAL_PIXEL_FORMAT_YCbCr_420_888, the returned stride must be * 0, since the actual strides are available from the android_ycbcr * structure. * * Returns 0 on success or -errno on error. */ int (*alloc)(struct alloc_device_t* dev, int w, int h, int format, int usage, buffer_handle_t* handle, int* stride); /* * (*free)() Frees a previously allocated buffer. * Behavior is undefined if the buffer is still mapped in any process, * but shall not result in termination of the program or security breaches * (allowing a process to get access to another process' buffers). * THIS FUNCTION TAKES OWNERSHIP of the buffer_handle_t which becomes * invalid after the call. * * Returns 0 on success or -errno on error. */ int (*free)(struct alloc_device_t* dev, buffer_handle_t handle); /* This hook is OPTIONAL. * * If non NULL it will be caused by SurfaceFlinger on dumpsys */ void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len); void* reserved_proc[7]; } alloc_device_t;
從其結構體成員能夠看出,其主要作用是為請求者分配圖形緩沖區。先看alloc函數的實現(gralloc.cpp):
static int gralloc_alloc(alloc_device_t* dev, int width, int height, int format, int usage, buffer_handle_t* pHandle, int* pStride) { if (!pHandle || !pStride) return -EINVAL; int bytesPerPixel = 0; switch (format) { case HAL_PIXEL_FORMAT_RGBA_FP16: bytesPerPixel = 8; break; case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_BGRA_8888: bytesPerPixel = 4; break; case HAL_PIXEL_FORMAT_RGB_888: bytesPerPixel = 3; break; case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_RAW16: bytesPerPixel = 2; break; default: return -EINVAL; } const size_t tileWidth = 2; const size_t tileHeight = 2; size_t stride = align(width, tileWidth); size_t size = align(height, tileHeight) * stride * bytesPerPixel + 4; int err; if (usage & GRALLOC_USAGE_HW_FB) { err = gralloc_alloc_framebuffer(dev, size, usage, pHandle); } else { err = gralloc_alloc_buffer(dev, size, usage, pHandle); } if (err < 0) { return err; } *pStride = stride; return 0; }
參數w表示要分配的圖形緩沖區所保存的圖像的寬度,將它乘以bpp,就能夠得到保存一行像素所須要使用的字節數。需要將這個字節數對齊到4個字節邊界,最后得到一行像素所須要的字節數就保存在變量bpr中。
參數h表示要分配的圖形緩沖區所保存的圖像的高度,將它乘以bpr。就能夠得到保存整個圖像所須要使用的字節數。將變量bpr的值除以變量bpp的值,就得到要分配的圖形緩沖區一行包括有多少個像素點,這個結果須要保存在輸出參數pStride中,以便能夠返回給調用者。
參數format用來描寫敘述要分配的圖形緩沖區的顏色格式,描寫敘述一個像素須要幾個字節來表示。
參數usage用來描寫敘述要分配的圖形緩沖區的用途,假設是用來在系統幀緩沖區中渲染的,即參數usage的GRALLOC_USAGE_HW_FB位等於1,那么就必須在系統幀緩沖區中分配,否則的話,就在內存中分配。注意,在內存中分配的圖形緩沖區,最終是需要復制到系統幀緩沖區去的,以便能夠將它所描寫敘述的圖形渲染出來。
函數gralloc_alloc_framebuffer用來在系統幀緩沖區中分配圖形緩沖區,而函數gralloc_alloc_buffer用來在內存在分配圖形緩沖區,接下來就來看看這兩個函數的實現。
gralloc_alloc_framebuffer最終調用了gralloc_alloc_framebuffer_locked(gralloc.cpp):
static int gralloc_alloc_framebuffer_locked(alloc_device_t* dev, size_t size, int usage, buffer_handle_t* pHandle) { private_module_t* m = reinterpret_cast<private_module_t*>( dev->common.module); // allocate the framebuffer if (m->framebuffer == NULL) { // initialize the framebuffer, the framebuffer is mapped once // and forever. int err = mapFrameBufferLocked(m); if (err < 0) { return err; } } const uint32_t bufferMask = m->bufferMask; const uint32_t numBuffers = m->numBuffers; const size_t bufferSize = m->finfo.line_length * m->info.yres; if (numBuffers == 1) { // If we have only one buffer, we never use page-flipping. Instead, // we return a regular buffer which will be memcpy'ed to the main // screen when post is called. int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; return gralloc_alloc_buffer(dev, bufferSize, newUsage, pHandle); } if (bufferMask >= ((1LU<<numBuffers)-1)) { // We ran out of buffers. return -ENOMEM; } // create a "fake" handles for it intptr_t vaddr = intptr_t(m->framebuffer->base); private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size, private_handle_t::PRIV_FLAGS_FRAMEBUFFER); // find a free slot for (uint32_t i=0 ; i<numBuffers ; i++) { if ((bufferMask & (1LU<<i)) == 0) { m->bufferMask |= (1LU<<i); break; } vaddr += bufferSize; } hnd->base = vaddr; hnd->offset = vaddr - intptr_t(m->framebuffer->base); *pHandle = hnd; return 0; }
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.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) { ALOGE("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; } } ALOGE_IF(err, "gralloc failed err=%s", strerror(-err)); return err; }
它首先調用函數ashmem_create_region來創建一塊匿名共享內存,接着再在這塊匿名共享內存上分配一個圖形緩沖區。注意,這個圖形緩沖區也是使用一個private_handle_t結構體來描述的,只是這個圖形緩沖區的標志值等於0,以區別於在系統幀緩沖區中分配的圖形緩沖區。當中mapBuffer又把hnd所描寫敘述的一個圖形緩沖區映射到當前進程的地址空間來。
以上內容就是alloc_device_t的相關內容。在private_module_t中有一個registerBuffer的函數指針,此函數是干什么的呢?在Android系統中,全部的圖形緩沖區都是由SurfaceFlinger服務分配的,而當一個圖形緩沖區被分配的時候,它會同一時候被映射到請求分配的進程的地址空間去,即分配過程同時包含了注冊過程,但對用戶空間的其他的應用程序來說,它們所須要的圖形緩沖區是在由SurfaceFlinger服務分配的,因此,當它們得到SurfaceFlinger服務分配的圖形緩沖區之后,還須要將這塊圖形緩沖區映射到自己的地址空間來,以便能夠使用這塊圖形緩沖區。
在系統幀緩沖區中分配的圖形緩沖區僅僅在SurfaceFlinger服務中使用,而SurfaceFlinger服務在初始化系統幀緩沖區的時候,已經將系統幀緩沖區映射到自己所在的進程中來了,因此,函數gralloc_map假設發現要注冊的圖形緩沖區是在系統幀緩沖區分配的時候,那么就無需再運行映射圖形緩沖區的操作了。假設要注冊的圖形緩沖區是在內存中分配的,即它的標志值flags的PRIV_FLAGS_FRAMEBUFFER位等於0,那么接下來就需要將它映射到當前進程的地址空間來,因為要注冊的圖形緩沖區是在文件描述符hnd->fd對應的一塊匿名共享內存中分配的,因此,僅僅需要將文件描述符hnd->fd對應的匿名共享內存映射到當前進程的地址空間來。因為映射文件描述符hnd->fd得到的是一整塊匿名共享內存在當前進程地址空間的基地址,而要注冊的圖形緩沖區可能僅僅占領這塊匿名共享內存的某一小部分,因此,還需要將要注冊的圖形緩沖區的在被映射的匿名共享內存中的偏移量hnd->offset加上被映射的匿名共享內存的基地址hnd->base,才能夠得到真正要注冊的圖形緩沖區在當前進程中的訪問地址,這個地址最終要被寫到hnd->base中去。