Android GUI系統學習1:Gralloc

Gralloc模塊是從Android Eclair(android 2.1)開始加入的一個HAL模塊,Gralloc的含義爲是Graphics Alloc(圖形分配)。他對上爲libui提供服務,爲其分配顯存,刷新顯示等。對下對framebuffer進行管理。java

gralloc代碼一般位於hardware/libhardware/modules/gralloc目錄下。包含如下幾個文件:android

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。app

以下所示(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):ide

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):post

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中去。

參考文檔:http://blog.csdn.net/luoshengyang/article/details/7747932

查看詳情

關注微信公衆平臺:程序員互動聯盟(coder_online),你能夠第一時間獲取原創技術文章,和(java/C/C++/Android/Windows/Linux)技術大牛作朋友,在線交流編程經驗,獲取編程基礎知識,解決編程問題。程序員互動聯盟,開發人員本身的家。

【Windows編程】系列第四篇:使用Unicode編程

相關文章
相關標籤/搜索