图形内存的申请与显示

这一篇回答序言中的第一个问题:

如何申请可以用来送显的内存,如何将其送往LCD?

要点

图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。

buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现图形内存的实际申请。

GraphicBuffer跨进程共享的流程是用binder传输必要信息到另一进程,另一进程调用gralloc模块的registerBuffer方法映射其到自己的内存空间。

GraphicBuffer与SurfaceFlinger 没有 直接关系,图形内存不仅仅是提供给窗口系统用的,也不是非得在SurfaceFlinger进程里申请。

调用 gralloc 模块的 post 函数指针,并在HAL层发送 FBIOPAN_DISPLAY 指令,是将图形内存送显的一个路径,但不是惟一。

GraphicBuffer

图形内存是用来渲染和显示的,它需要被跨进程共享,并支持不同硬件(GPU、DSP等)使用。

Android中的图形内存包裹类为GraphicBuffer。

c73865997f9cda08e63e62b2124568a7.png

GraphicBuffer继承于ANativeWindowBuffer。

ANativeWindowBuffer包含w,h,stride和handle属性,其中handle对应一个private_handle_t的指针。

GraphicBuffer额外包括 mBufferMapper(映射器,为单例),mId(每块GraphicBuffer的独立id,根据进程号与申请顺序编号),mInitCheck(表示申请状态,用来检查是否实际申请到了内存)。

GraphicBuffer申请流程

申请释放图形内存的流程如下:

29bcfba36e86da00d295042c7f24970e.png

GraphicBuffer共享流程

尽管已经将GraphicBuffer映射到了自己的进程空间,在进一步使用时,流程上需要在使用前lock,使用完后unlock,这两个步骤一般用来作cache同步(根据共享内存策略,如果是缓存式,CPU/GPU会先把数据写到缓存,达到一定量才同步到GraphicBuffer中,unlock时可以强制把缓存同步一次)。

标志flags介绍

在 GraphicBuffer申请及lock的参数里,有个flags属性值,gralloc模块根据这个值,去判断从什么地方申请内存,按什么方式组织内存,我们来看一下:

USAGE_SW_READ_NEVER = GRALLOC_USAGE_SW_READ_NEVER,

USAGE_SW_READ_RARELY = GRALLOC_USAGE_SW_READ_RARELY,

USAGE_SW_READ_OFTEN = GRALLOC_USAGE_SW_READ_OFTEN,

USAGE_SW_READ_MASK = GRALLOC_USAGE_SW_READ_MASK,

USAGE_SW_WRITE_NEVER = GRALLOC_USAGE_SW_WRITE_NEVER,

USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY,

USAGE_SW_WRITE_OFTEN = GRALLOC_USAGE_SW_WRITE_OFTEN,

USAGE_SW_WRITE_MASK = GRALLOC_USAGE_SW_WRITE_MASK,

USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK,

USAGE_PROTECTED = GRALLOC_USAGE_PROTECTED,

USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE,

USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER,

USAGE_HW_2D = GRALLOC_USAGE_HW_2D,

USAGE_HW_COMPOSER = GRALLOC_USAGE_HW_COMPOSER,

USAGE_HW_VIDEO_ENCODER= GRALLOC_USAGE_HW_VIDEO_ENCODER,

USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK,

USAGE_CURSOR = GRALLOC_USAGE_CURSOR,

GRALLOC_USAGE_SW_READ_NEVER,GRALLOC_USAGE_SW_READ_RARELY,GRALLOC_USAGE_SW_READ_OFTEN

分别表示CPU不需要/很少会/经常会读这块GraphicBuffer,对于READ_OFTEN经常读的情况,gralloc模块应该考虑建读缓存了。

CPU写的三个标志类似。

当发觉GraphicBuffer操作起来速度慢时,就得看一下,是不是忘了配CPU的读写标志了。

GRALLOC_USAGE_HW_TEXTURE

GRALLOC_USAGE_HW_RENDER

这两个标志分别表示需要GPU读与写,TEXTURE表示可以映射为一个OpenGL的纹理,RENDER表示可以作为OpenGL的渲染目标。

一般来说,gralloc分配的内存都是gpu可读写的,也不需要加这两个标志。

GRALLOC_USAGE_HW_COMPOSER

这个表示这个GraphicBuffer可以由硬件合成器直接合成。

GRALLOC_USAGE_HW_VIDEO_ENCODER

这个表示这个GraphicBuffer可以作为Video硬解码(一般是DSP)的输入输出对象。

Gralloc

gralloc模块需要实现如下三个设备及函数指针。

内存分配

typedef struct alloc_device_t {

struct hw_device_t common;

int (*alloc)(struct alloc_device_t* dev,

int w, int h, int format, int usage,

buffer_handle_t* handle, int* stride);

int (*free)(struct alloc_device_t* dev,

buffer_handle_t handle);

} alloc_device_t;

内存共享

typedef struct gralloc_module_t {

......

int (*registerBuffer)(struct gralloc_module_t const* module,

buffer_handle_t handle);

int (*unregisterBuffer)(struct gralloc_module_t const* module,

buffer_handle_t handle);

int (*lock)(struct gralloc_module_t const* module,

buffer_handle_t handle, int usage,

int l, int t, int w, int h,

void** vaddr);

int (*unlock)(struct gralloc_module_t const* module,

buffer_handle_t handle);

......

}

显示

typedef struct framebuffer_device_t {

struct hw_device_t common;

......

int (*setSwapInterval)(struct framebuffer_device_t* window, int interval);//设置刷新频率

int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);//设置更新区域,对于带缓存的LCD屏,可以在只传发生了变化的区域过去,此即局部刷新。

int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);//送显

......

} framebuffer_device_t;

关于gralloc模块如何注册如何打开,可看老罗的博客:

http://blog.csdn.net/luoshengyang/article/details/7747932

这里面需要校正的是 hardware/libhardware/modules/gralloc下面的代码编译出来的是gralloc.default.so,即Android系统默认提供的。一般来说厂商不会用这个so。

如下是Arm为Mali系列gpu提供的开源gralloc代码的链接,相对而言更有参考意义。

http://malideveloper.arm.com/cn/develop-for-mali/drivers/open-source-mali-gpus-android-gralloc-module/

Arm提供的代码里面包括自家的ump方案和标准的ion方案,一般而言,在Android4.2之后,普遍用的都是ion方案,ion内存共享方案可参考此博客的文章。

http://blog.csdn.net/qq160816/article/details/38082579

http://blog.csdn.net/qq160816/article/details/38299251

我们只看一下这份代码里面送显的部分:

static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)

{

if (private_handle_t::validate(buffer) < 0)

{

return -EINVAL;

}

private_handle_t const* hnd = reinterpret_cast(buffer);

private_module_t* m = reinterpret_cast(dev->common.module);

if (m->currentBuffer)

{

m->base.unlock(&m->base, m->currentBuffer);

m->currentBuffer = 0;

}

if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)

{

m->base.lock(&m->base, buffer, private_module_t::PRIV_USAGE_LOCKED_FOR_POST,

0, 0, m->info.xres, m->info.yres, NULL);

const size_t offset = hnd->base - m->framebuffer->base;

int interrupt;

m->info.activate = FB_ACTIVATE_VBL;

m->info.yoffset = offset / m->finfo.line_length;

#ifdef STANDARD_LINUX_SCREEN

#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)

#define S3CFB_SET_VSYNC_INT _IOW('F', 206, unsigned int)

if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1)

{

AERR( "FBIOPAN_DISPLAY failed for fd: %d", m->framebuffer->fd );

m->base.unlock(&m->base, buffer);

return 0;

}

{

// enable VSYNC

interrupt = 1;

if(ioctl(m->framebuffer->fd, S3CFB_SET_VSYNC_INT, &interrupt) < 0)

{

AERR( "S3CFB_SET_VSYNC_INT enable failed for fd: %d", m->framebuffer->fd );

return 0;

}

// wait for VSYNC

#ifdef MALI_VSYNC_EVENT_REPORT_ENABLE

gralloc_mali_vsync_report(MALI_VSYNC_EVENT_BEGIN_WAIT);

#endif

int crtc = 0;

if(ioctl(m->framebuffer->fd, FBIO_WAITFORVSYNC, &crtc) < 0)

{

AERR( "FBIO_WAITFORVSYNC failed for fd: %d", m->framebuffer->fd );

#ifdef MALI_VSYNC_EVENT_REPORT_ENABLE

gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);

#endif

return 0;

}

#ifdef MALI_VSYNC_EVENT_REPORT_ENABLE

gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);

#endif

// disable VSYNC

interrupt = 0;

if(ioctl(m->framebuffer->fd, S3CFB_SET_VSYNC_INT, &interrupt) < 0)

{

AERR( "S3CFB_SET_VSYNC_INT disable failed for fd: %d", m->framebuffer->fd );

return 0;

}

}

#else

/*Standard Android way*/

#ifdef MALI_VSYNC_EVENT_REPORT_ENABLE

gralloc_mali_vsync_report(MALI_VSYNC_EVENT_BEGIN_WAIT);

#endif

if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1)

{

AERR( "FBIOPUT_VSCREENINFO failed for fd: %d", m->framebuffer->fd );

#ifdef MALI_VSYNC_EVENT_REPORT_ENABLE

gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);

#endif

m->base.unlock(&m->base, buffer);

return -errno;

}

#ifdef MALI_VSYNC_EVENT_REPORT_ENABLE

gralloc_mali_vsync_report(MALI_VSYNC_EVENT_END_WAIT);

#endif

#endif

m->currentBuffer = buffer;

}

else

{

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;

}

送显的核心代码是这一句:

if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1)

再往下看得跟厂商的内核代码了,这里没代码略过。关于LCD显示原理看老罗那篇就好了。

调用gralloc模块的post函数不是惟一一种将图形内存送到LCD显示的方法,另一种方式是Overlay(hwcomposer的硬件合成)。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐