Android 显示系统:OpenGL 和 Gralloc

时间:2022-06-17 14:50:08

 一、OpenGL ES与EGL

  Android的GUI系统是基于OpenGL/EGL来实现的。

  1. 由于OpenGL是通用函数库,在不同平台系统上需要被“本土化”——把它与具体平台的窗口系统建立起关联,FramebufferNativeWindow是负责OpenGL ES在Android平台上本地化的中介之一。为OpenGL ES配置本地窗口的是EGL,EGL介于本地窗口系统和Rendering API(即OPenGL ES)之间的一层接口。

  2. 与OpenGL相关的模块可分为:
    (1)配置类。 帮助OpenGL ES完成配置的,包括EGL、DisplayHardware都归为此类。
    (2)依赖类。 OpenGL ES正常运行所依赖的“本地化”的东西,如FramebufferNativeWindow。
    (3)使用类。 使用者也可能是配置者,如DisplayDevice既扮演了构建OpenGL ES环境的角色,同时也是它的用户。
      OpenGL相关知识可查看Android:OpenGL ES开发入门篇(待写)

  Android 显示系统:OpenGL 和 Gralloc

 

二、终端显示设备 - Gralloc与Framebuffer

  Linux内核提供了统一的framebuffer显示驱动。Framebuffer是内核系统提供的图形硬件的抽象描述,称为buffer是因为它也占用了系统存储空间的一部分,是一块包含屏幕显示信息的缓冲区。Framebuffer借助于Linux文件系统向上层应用提供了统一而高效的操作接口,让用户空间运行的程序比较容易地适配多种显示设备。
  Android系统中,framebuffer提供的设备节点为/dev/graphics/fb或者/dev/fb,其中fb0表示第一个主显示屏幕,必须存在,当前系统实现中只用到了一个显示屏。显示系统使用HAL层间接引用底层架构,从而操作帧缓冲区。而完成这一中介任务的就是Gralloc.下图为Gralloc的模块简图,描述了gpu0和fb0的主要API.

Android 显示系统:OpenGL 和 Gralloc
                        Gralloc API架构
 
  Gralloc模块是从Android Eclair(android 2.1)開始增加的一个HAL模块,Gralloc的含义为是Graphics Alloc(图形分配)。他对上为libui提供服务,为其分配显存,刷新显示等。对下对framebuffer进行管理。
  Gralloc对应的模块是由FramebufferNativeWindow在构造函数加载的,Gralloc包括fb和gralloc两个设备,前者负责打开内核中的Framebuffer、初始化配置,并提供了post、setSwapInterval等操作接口。后者则管理帧缓冲区的分配和释放。
  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。

例如以下所看到的(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 = %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主要做了以下几件事情:
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
  
看之前的HAL模块比方Camera模块,有一个hw_module_t结构和一个hw_device_t结构,而这里的gralloc模块却包括了两个hw_device_t结构,一个alloc_device_t和一个framebuffer_device_t结构。先看framebuffer_device_t定义:

framebuffer_device_t(fb.h):

typedef struct 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;

从这个结构看以看出,framebuffer_device_t里面主要保存了framebuffer相关的一些信息,比如分辨率。刷新率,framebuffer的数量等。另外,里面定义了一些操作framebuffer的函数,一下简介当中几个函数。

1. static int fb_setSwapInterval(struct framebuffer_device_t* dev, 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的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去。

函数实现例如以下:

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的函数定义能够看出。事实上现方式有两种方式。第一种方式是把Framebuffer的后buffer切为前buffer。然后通过IOCTRL机制告诉FB驱动切换DMA源地地址。

详细原理是这种:当private_handle_t结构体hnd所描写叙述的图形缓冲区是在系统帧缓冲区中分配的时候,即这个图形缓冲区的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于1的时候。我们是不须要将图形缓冲区的内容复制到系统帧缓冲区去的,由于我们将内容写入到图形缓冲区的时候,已经相当于是将内容写入到了系统帧缓冲区中去了。尽管在这种情况下。我们不须要将图形缓冲区的内容复制到系统帧缓冲区去,可是我们须要告诉系统帧缓冲区设备将要渲染的图形缓冲区作为系统当前的输出图形缓冲区,这样才干够将要渲染的图形缓冲区的内容绘制到设备显示屏来。比如。如果系统帧缓冲区有2个图形缓冲区,当前是以第1个图形缓冲区作为输出图形缓冲区的,这时候如果我们须要渲染第2个图形缓冲区,那么就必须告诉系统帧绘冲区设备,将第2个图形缓冲区作为输出图形缓冲区。这个实现方式的前提是Linux内核必须分配至少两个缓冲区大小的物理内存和实现切换的ioctrol。这个比較高速。

设置系统帧缓冲区的当前输出图形缓冲区是通过IO控制命令FBIOPUT_VSCREENINFO来进行的。

IO控制命令FBIOPUT_VSCREENINFO须要一个fb_var_screeninfo结构体作为參数。

从前面第3部分的内容能够知道,private_module_t结构体m的成员变量info正好保存在我们所须要的这个fb_var_screeninfo结构体。有了个m->info这个fb_var_screeninfo结构体之后,我们仅仅须要设置好它的成员变量yoffset的值(不用设置成员变量xoffset的值是由于全部的图形缓冲区的宽度是相等的),就能够将要渲染的图形缓冲区设置为系统帧缓冲区的当前输出图形缓冲区。fb_var_screeninfo结构体的成员变量yoffset保存的是当前输出图形缓冲区在整个系统帧缓冲区的纵向偏移量,即Y偏移量。

我们仅仅须要将要渲染的图形缓冲区的開始地址hnd->base的值减去系统帧缓冲区的基地址m->framebuffer->base的值,再除以图形缓冲区一行所占领的字节数m->finfo.line_length,就能够得到所须要的Y偏移量。

在运行IO控制命令FBIOPUT_VSCREENINFO之前,还会将作为參数的fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL,表示要等到下一个垂直同步事件出现时。再将当前要渲染的图形缓冲区的内容绘制出来。

这样做的目的是避免出现屏幕闪烁。即避免前后两个图形缓冲区的内容各有一部分同一时候出现屏幕中。

另外一种方式是利用copy的方式来实现,比較耗时。当private_handle_t结构体hnd所描写叙述的图形缓冲区是在内存中分配的时候。即这个图形缓冲区的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于0的时候,我们就须要将它的内容复制到系统帧缓冲区中去了。这个拷贝的工作是通过调用函数memcpy来完毕的。

在拷贝之前。我们须要三个參数。第一个參数是要渲染的图形缓冲区的起址地址。这个地址保存在參数buffer所指向的一个private_handle_t结构体中。第二个參数是要系统帧缓冲区的基地址,这个地址保存在private_module_t结构体m的成员变量framebuffer所指向的一个private_handle_t结构体中。

第三个參数是要拷贝的内容的大小,这个大小就刚好是一个屏幕像素所占领的内存的大小。

屏幕高度由m->info.yres来描写叙述,而一行屏幕像素所占用的字节数由m->finfo.line_length来描写叙述,将这两者相乘。就能够得到一个屏幕像素所占领的内存的大小。

在将一块内存缓冲区的内容复制到系统帧缓冲区中去之前,须要对这两块缓冲区进行锁定。以保证在拷贝的过程中。这两块缓冲区的内容不会被改动。这个锁定的工作是由Gralloc模块中的函数gralloc_lock来实现的。

从前面第1部分的内容能够知道,Gralloc模块中的函数gralloc_lock的地址正好就保存在private_module_t结构体m的成员变量base所描写叙述的一个gralloc_module_t结构体的成员函数lock中。


在调用函数gralloc_lock来锁定一块缓冲区之后,还能够通过最后一个输出參数来获得被锁定的缓冲区的開始地址。因此。通过调用函数gralloc_lock来锁定要渲染的图形缓冲区以及系统帧缓冲区。就能够得到前面所须要的第一个和第二个參数。

将要渲染的图形缓冲区的内容复制到系统帧缓冲区之后,就能够解除前面对它们的锁定了。这个解锁的工作是由Gralloc模块中的函数gralloc_unlock来实现的。从前面第1部分的内容能够知道,Gralloc模块中的函数gralloc_unlock的地址正好就保存在private_module_t结构体m的成员变量base所描写叙述的一个gralloc_module_t结构体的成员函数unlock中。

以上是framebuffer_device_t结构相关的一些内容,其主要作用是渲染图形缓冲区来显示内容。以下在看看alloc_device_t的内容。
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 w, int h, int format, int usage,
 buffer_handle_t* pHandle, int* pStride)
{
 if (!pHandle || !pStride)
 return -EINVAL;

size_t size, stride;

int align = 4;
 int bpp = 0;
 switch (format) {
 case HAL_PIXEL_FORMAT_RGBA_8888:
 case HAL_PIXEL_FORMAT_RGBX_8888:
 case HAL_PIXEL_FORMAT_BGRA_8888:
 bpp = 4;
 break;
 case HAL_PIXEL_FORMAT_RGB_888:
 bpp = 3;
 break;
 case HAL_PIXEL_FORMAT_RGB_565:
 case HAL_PIXEL_FORMAT_RAW_SENSOR:
 bpp = 2;
 break;
 default:
 return -EINVAL;
 }
 size_t bpr = (w*bpp + (align-1)) & ~(align-1);
 size = bpr * h;
 stride = bpr / bpp;

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;
}

參数format用来描写叙述要分配的图形缓冲区的颜色格式,描写叙述一个像素须要几个字节来表示。參数w表示要分配的图形缓冲区所保存的图像的宽度,将它乘以bpp,就能够得到保存一行像素所须要使用的字节数。我们须要将这个字节数对齐到4个字节边界,最后得到一行像素所须要的字节数就保存在变量bpr中。

參数h表示要分配的图形缓冲区所保存的图像的高度,将它乘以bpr。就能够得到保存整个图像所须要使用的字节数。将变量bpr的值除以变量bpp的值,就得到要分配的图形缓冲区一行包括有多少个像素点,这个结果须要保存在输出參数pStride中,以便能够返回给调用者。

參数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位等于1,那么接下来就须要将它映射到当前进程的地址空间来了。因为要注冊的图形缓冲区是在文件描写叙述符hnd->fd所描写叙述的一块匿名共享内存中分配的。因此,我们仅仅须要将文件描写叙述符hnd->fd所描写叙述的一块匿名共享内存映射到当前进程的地址空间来。就能够将參数hnd所描写叙述的一个图形缓冲区映射到当前进程的地址空间来。

因为映射文件描写叙述符hnd->fd得到的是一整块匿名共享内存在当前进程地址空间的基地址,而要注冊的图形缓冲区可能仅仅占领这块匿名共享内存的某一小部分,因此,我们还须要将要注冊的图形缓冲区的在被映射的匿名共享内存中的偏移量hnd->offset加上被映射的匿名共享内存的基地址hnd->base。才干够得到要注冊的图形缓冲区在当前进程中的訪问地址。这个地址终于又被写入到hnd->base中去。