Android图形显示系统——下层显示2:图形内存的申请与显示

简介: 图形内存的申请与显示这一篇回答序言中的第一个问题: 如何申请可以用来送显的内存,如何将其送往LCD?要点图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现图形内存的实际申请。GraphicBuffer跨

图形内存的申请与显示

这一篇回答序言中的第一个问题:
如何申请可以用来送显的内存,如何将其送往LCD?

要点

  1. 图形内存是进程共享内存,且根据其标志支持不同硬件设备的读与写。
  2. buffer_handle_t 是 *private_handle_t,gralloc模块自定义private_handle_t类型,并实现图形内存的实际申请。
  3. GraphicBuffer跨进程共享的流程是用binder传输必要信息到另一进程,另一进程调用gralloc模块的registerBuffer方法映射其到自己的内存空间。
  4. GraphicBuffer与SurfaceFlinger 没有 直接关系,图形内存不仅仅是提供给窗口系统用的,也不是非得在SurfaceFlinger进程里申请。
  5. 调用 gralloc 模块的 post 函数指针,并在HAL层发送 FBIOPAN_DISPLAY 指令,是将图形内存送显的一个路径,但不是惟一。

GraphicBuffer

图形内存是用来渲染和显示的,它需要被跨进程共享,并支持不同硬件(GPU、DSP等)使用。
Android中的图形内存包裹类为GraphicBuffer。
GraphicBuffer
GraphicBuffer继承于ANativeWindowBuffer。
ANativeWindowBuffer包含w,h,stride和handle属性,其中handle对应一个private_handle_t的指针。
GraphicBuffer额外包括 mBufferMapper(映射器,为单例),mId(每块GraphicBuffer的独立id,根据进程号与申请顺序编号),mInitCheck(表示申请状态,用来检查是否实际申请到了内存)。

GraphicBuffer申请流程

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

GraphicBuffer共享流程

Map流程
尽管已经将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<private_handle_t const*>(buffer);
    private_module_t* m = reinterpret_cast<private_module_t*>(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的硬件合成)。

相关实践学习
在云上部署ChatGLM2-6B大模型(GPU版)
ChatGLM2-6B是由智谱AI及清华KEG实验室于2023年6月发布的中英双语对话开源大模型。通过本实验,可以学习如何配置AIGC开发环境,如何部署ChatGLM2-6B大模型。
目录
相关文章
|
3月前
|
Linux 测试技术 语音技术
【车载Android】模拟Android系统的高负载环境
本文介绍如何将Linux压力测试工具Stress移植到Android系统,用于模拟高负载环境下的CPU、内存、IO和磁盘压力,帮助开发者优化车载Android应用在多任务并发时的性能问题,提升系统稳定性与用户体验。
278 6
|
3月前
|
Java 数据库 Android开发
基于Android的电子记账本系统
本项目研究开发一款基于Java与Android平台的开源电子记账系统,采用SQLite数据库和Gradle工具,实现高效、安全、便捷的个人财务管理,顺应数字化转型趋势。
|
4月前
|
缓存 监控 Linux
CentOS系统如何查看当前内存容量。
以上方法都不需要特殊软件或者复杂配置即可执行,在CentOS或其他Linux发行版中都适合运行,并且它们各自透露出不同角度对待问题解答方式:从简单快速到深入详尽;从用户态到核心态;从操作层数到硬件层数;满足不同用户需求与偏好。
355 8
|
5月前
|
存储 缓存 监控
手动清除Ubuntu系统中的内存缓存的步骤
此外,只有系统管理员或具有适当权限的用户才能执行这些命令,因为这涉及到系统级的操作。普通用户尝试执行这些操作会因权限不足而失败。
970 22
|
8月前
|
安全 搜索推荐 Android开发
Android系统SELinux安全机制详解
如此看来,SELinux对于大家来说,就像那位不眠不休,严阵以待的港口管理员,守护我们安卓系统的平安,维护这片海港的和谐生态。SELinux就这样,默默无闻,却卫士如山,给予Android系统一份厚重的安全保障。
288 18
|
9月前
|
监控 Linux Python
Linux系统资源管理:多角度查看内存使用情况。
要知道,透过内存管理的窗口,我们可以洞察到Linux系统运行的真实身姿,如同解剖学家透过微观镜,洞察生命的奥秘。记住,不要惧怕那些高深的命令和参数,他们只是你掌握系统"魔法棒"的钥匙,熟练掌握后,你就可以骄傲地说:Linux,我来了!
323 27
|
机器学习/深度学习 人工智能 缓存
【AI系统】推理内存布局
本文介绍了CPU和GPU的基础内存知识,NCHWX内存排布格式,以及MNN推理引擎如何通过数据内存重新排布进行内核优化,特别是针对WinoGrad卷积计算的优化方法,通过NC4HW4数据格式重排,有效利用了SIMD指令集特性,减少了cache miss,提高了计算效率。
446 3
|
2月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
319 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
2月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
292 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
2月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
670 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡

热门文章

最新文章