Android 显示系统:OpenGL简介和Gralloc代码分析
一、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.
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的调用开始分析代码(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(), mHwc(), mNumDisplays(),
mCBContext(new cb_context),
mEventHandler(handler),
mDebugForceFakeVSync(false)
{
for (size_t i = ; i<MAX_HWC_DISPLAYS ; i++) {
mLists[i] = ;
} for (size_t i= ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
mLastHwVSync[i] = ;
mVSyncCounts[i] = ;
} char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.no_hw_vsync", value, "");
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= ; i<NUM_BUILTIN_DISPLAYS ; i++) {
mAllocatedDisplayIDs.markBit(i);
} if (mHwc) {
ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
(hwcApiVersion(mHwc) >> ) & 0xff,
(hwcApiVersion(mHwc) >> ) & 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, , 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, ); // 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 = ;
}
} 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 = ;
} else if (mHwc) {
// here we're guaranteed to have at least HWC 1.1
for (size_t i = ; 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 != ) {
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 = ;
char prop[PATH_MAX] = {};
char path[PATH_MAX] = {};
char name[PATH_MAX] = {};
char prop_name[PATH_MAX] = {}; 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) > ) {
if (hw_module_exists(path, sizeof(path), name, prop) == ) {
goto found;
}
} /* Loop through the configuration variants looking for a module */
for (i= ; i<HAL_VARIANT_KEYS_COUNT; i++) {
if (property_get(variant_keys[i], prop, NULL) == ) {
continue;
}
if (hw_module_exists(path, sizeof(path), name, prop) == ) {
goto found;
}
} /* Nothing found, try the default */
if (hw_module_exists(path, sizeof(path), name, "default") == ) {
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/", ) == ) {
/* 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) != ) {
ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
} hmi->dso = handle; /* success */
status = ; done:
if (status != ) {
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));
}
其中调用了上面结构体中初始化的open函数,open函数指向gralloc.cpp中的gralloc_device_open函数:
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, , sizeof(*dev)); /* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = ;
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 = ;
} 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, , sizeof(*dev)); /* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = ;
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 = ; private_module_t* m = (private_module_t*)module;
status = mapFrameBuffer(m);
if (status >= ) {
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> );
int format = (m->info.bits_per_pixel == )
? (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) = ;
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) = ;
const_cast<int&>(dev->device.maxSwapInterval) = ;
*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 ;
} char const * const device_template[] = {
"/dev/graphics/fb%u",
"/dev/fb%u",
}; int fd = -;
int i=;
char name[]; while ((fd==-) && device_template[i]) {
snprintf(name, , device_template[i], );
fd = open(name, O_RDWR, );
i++;
}
if (fd < )
return -errno; struct fb_fix_screeninfo finfo;
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -)
return -errno; struct fb_var_screeninfo info;
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -)
return -errno; info.reserved[] = ;
info.reserved[] = ;
info.reserved[] = ;
info.xoffset = ;
info.yoffset = ;
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) == -) {
ALOGW("FBIOPAN_DISPLAY failed, page flipping not supported");
#else
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -) {
ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
#endif
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
} if (info.yres_virtual < info.yres * ) {
// 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*);
} if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -)
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 > ? (int)(1000000000000000LLU / refreshQuotient) : ; if (refreshRate == ) {
// bleagh, bad info from the driver
refreshRate = *; // 60 Hz
} if (int(info.width) <= || int(info.height) <= ) {
// 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) == -)
return -errno; if (finfo.smem_len <= )
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, ); module->numBuffers = info.yres_virtual / info.yres;
module->bufferMask = ; void* vaddr = mmap(, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, );
if (vaddr == MAP_FAILED) {
ALOGE("Error mapping the framebuffer (%s)", strerror(errno));
return -errno;
}
module->framebuffer->base = intptr_t(vaddr);
memset(vaddr, , fbSize);
return ;
}
由上面函数看出。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[]; /*
* 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[]; } 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的函数:
. int (*setSwapInterval)(struct framebuffer_device_t* window,
int interval); // 这个函数基本没实用,由于maxSwapInterval=minSwapInterval= 1。
. int (*setUpdateRect)(struct framebuffer_device_t* window,
int left, int top, int width, int height); // 局部刷新用的,默认没有启用,和平台有关。
. 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) < )
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) == -) {
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,
, , m->info.xres, m->info.yres,
&fb_vaddr); m->base.lock(&m->base, buffer,
GRALLOC_USAGE_SW_READ_RARELY,
, , 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 ;
}
从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[];
} 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 = ;
switch (format) {
case HAL_PIXEL_FORMAT_RGBA_FP16:
bytesPerPixel = ;
break;
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_BGRA_8888:
bytesPerPixel = ;
break;
case HAL_PIXEL_FORMAT_RGB_888:
bytesPerPixel = ;
break;
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_RAW16:
bytesPerPixel = ;
break;
default:
return -EINVAL;
} const size_t tileWidth = ;
const size_t tileHeight = ; size_t stride = align(width, tileWidth);
size_t size = align(height, tileHeight) * stride * bytesPerPixel + ; 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 < ) {
return err;
} *pStride = stride;
return ;
}
参数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 < ) {
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 == ) {
// 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)-)) {
// 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= ; i<numBuffers ; i++) {
if ((bufferMask & (1LU<<i)) == ) {
m->bufferMask |= (1LU<<i);
break;
}
vaddr += bufferSize;
} hnd->base = vaddr;
hnd->offset = vaddr - intptr_t(m->framebuffer->base);
*pHandle = hnd; return ;
}
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 = ;
int fd = -; size = roundUpToPageSize(size); fd = ashmem_create_region("gralloc-buffer", size);
if (fd < ) {
ALOGE("couldn't create ashmem (%s)", strerror(-errno));
err = -errno;
} if (err == ) {
private_handle_t* hnd = new private_handle_t(fd, size, );
gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
dev->common.module);
err = mapBuffer(module, hnd);
if (err == ) {
*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中去。
Android 显示系统:OpenGL简介和Gralloc代码分析的更多相关文章
- 11.5 Android显示系统框架_Vsync机制_代码分析
5.5 surfaceflinger对vsync的处理buffer状态图画得不错:http://ju.outofmemory.cn/entry/146313 android设备可能连有多个显示器,AP ...
- Android 显示系统:SurfaceFlinger详解
一.Android系统启动 Android设备从按下开机键到桌面显示画面,大致过程如下图流程: 开机显示桌面.从桌面点击 App 图标到 Activity显示在屏幕上的过程又是怎样的呢?下面介绍And ...
- 11.1 Android显示系统框架_framebuffer原理及改进
1. Android显示系统框架Android Graphic UI with GPU Hardware Accelerationhttps://community.nxp.com/docs/DOC- ...
- Android GUI系统学习1:Gralloc
Gralloc模块是从Android Eclair(android 2.1)開始增加的一个HAL模块,Gralloc的含义为是Graphics Alloc(图形分配).他对上为libui提供服务,为其 ...
- Android 显示系统:Vsync机制
一.Vsync简介: 屏幕的刷新过程是每一行从左到右(行刷新,水平刷新,Horizontal Scanning),从上到下(屏幕刷新,垂直刷新,Vertical Scanning).当整个屏幕刷新完毕 ...
- 11.5 Android显示系统框架_Vsync机制_黄油计划_三个方法改进显示系统
5. Vsync机制5.1 黄油计划_三个方法改进显示系统vsync, triple buffering, vsync虚拟化 参考文档:林学森 <深入理解Android内核设计思想>第2版 ...
- 11.2 Android显示系统框架_android源码禁用hwc和GPU
2. 修改tiny4412_Android源码禁用hwc和gpu(厂家不会提供hwc和gpu的源代码,没有源代码就没法分析了,因此在这里禁用该功能并用软件库实现)最终源码: git clone htt ...
- Android动画学习(一)——Android动画系统框架简介
2015-11-09补充:Drawable Animation极有可能是Frame Animation 这几天在找工作,面试的时候被问到了Android动画,之前完全没接触过这部分,直接给懵了,当然其 ...
- Android 显示系统:飞思卡尔平台图形界面与GPU硬件加速
图形是Android平台中的一个大主题,包含java/jni图形框架和2d/3d图形引擎(skia.OpenGL-ES.renderscript). 本文档描述了飞思卡尔设备上的一般Android图形 ...
随机推荐
- C#单元测试小例子
步骤简略,特别处进行说明. 以VS2015为例. 1.创建一个解决方案方案,如添加一个ConsoleApplication1的解决方案. 2.在解决方案的默认项目中,添加一个Calc类 3.将Calc ...
- Web服务器主动推送技术
HTTP协议遵循经典的客户端-服务器模型,客户端发送一个请求,然后等待服务器端的响应,服务器端只能在接收到客户端的请求之后进行响应,不能主动的发送数据到客户端. 客户端想要在不刷新页面的情况下实时获取 ...
- ubuntu下后台服务的管理
注册后台服务后都有脚本存在/lib/systemd/system路径下 如mysql为mysql.service 被service管理的进程被杀掉还是会重启的 应使用service XXX stop停 ...
- MVC,MVP 和 MVVM 的图示 - 阮一峰
作者: 阮一峰 日期: 2015年2月 1日 复杂的软件必须有清晰合理的架构,否则无法开发和维护. MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用.它本身 ...
- windows下mysql5.6.x的日志正确配置方法(my.ini) (网上的都是5.6之前的版本)
https://blog.csdn.net/databatman/article/details/49951853 感谢楼主,找了好久,试了一下楼主的,果然是对的,网上的日志配置都是5.6之前的版本: ...
- 系统分析与设计结对项目——WordCount
结对项目完成WordCount 合作者:201631062507 201631062526(学号) 代码地址:https://gitee.com/WordCountMC/WordCountTeam ...
- Python 实践--混入类
Mix-in:混入类是一种Python程序设计中的技术,作用是在运行期间动态改变类的基类或类的方法,从而使得类的表现可以发生变化.可以用在一个通用类接口中. 在实践一个 解析XML文件的实践中,体会动 ...
- P4053 [JSOI2007]建筑抢修 堆贪心
思路:堆贪心 提交:1次 题解: 先按时间\(sort\),然后如果能修就直接扔堆里,不能修取堆顶比一下时间长短,把时间短的扔进堆: #include<cstdio> #include&l ...
- 「CF484E」Sign on Fence「整体二分」「线段树」
题意 给定一个长度为\(n\)的正整数序列,第\(i\)个数为\(h_i\),\(m\)个询问,每次询问\((l, r, w)\),为\([l, r]\)所有长度为\(w\)的子区间最小值的最大值.( ...
- Til the Cows Come Home ( POJ 2387) (简单最短路 Dijkstra)
problem Bessie is out in the field and wants to get back to the barn to get as much sleep as possibl ...