您的位置:首页 > Web前端

SurfaceFlinger GraphicBuffer内存共享缓冲区机制

2016-04-10 22:24 417 查看
http://blog.csdn.net/andyhuabing/article/details/7489776

前两周比较忙,没时间写下这篇博客

GraphicBuffer 是 Surface 系统中用于GDI内存共享缓冲区管理类,封装了与硬件相关的细节,从而简化应用层的处理逻辑

SurfaceFlinger是个服务端,而每个请求服务的应用程序都对应一个Client端,Surface绘图由Client进行,而由SurfaceFlinger对所有Client绘制的图合成进行输出,那么这两者是如何共享这块图形缓冲区的内存呢?简要之就是利用mmap/ummap,那么这些在android系统中是如何构架完成的呢?

frameworks\base\include\ui\GraphicBuffer.h 类定义:

class GraphicBuffer

  : public EGLNativeBase<

      android_native_buffer_t, 

      GraphicBuffer, 

      LightRefBase<GraphicBuffer> >, public Flattenable

EGLNativeBase 是一个模板类:

template <typename NATIVE_TYPE, typename TYPE, typename REF>

class EGLNativeBase : public NATIVE_TYPE, public REF

类 GraphicBuffer  继承LightRefBase支持轻量级引用计数控制

   派生 Flattenable 用于数据序列化给Binder进行传输

   

我们来看下 android_native_buffer.h 文件,这个 android_native_buffer_t 结构:

typedef struct android_native_buffer_t

{

#ifdef __cplusplus

    android_native_buffer_t() { 

        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;

        common.version = sizeof(android_native_buffer_t);

        memset(common.reserved, 0, sizeof(common.reserved));

    }

#endif

    struct android_native_base_t common;

    int width;

    int height;

    int stride;

    int format;

    int usage;

    

    void* reserved[2];

    buffer_handle_t handle;

    void* reserved_proc[8];

} android_native_buffer_t;

注意这里有个关键的变量: buffer_handle_t handle; 这个就是显示内存分配与管理的私有数据结构

1、 native_handle_t 对 private_handle_t 的包裹

typedef struct

{

    int version;        /* sizeof(native_handle_t) */

    int numFds;         /* number of file-descriptors at &data[0] */

    int numInts;        /* number of ints at &data[numFds] */

    int data[0];        /* numFds + numInts ints */  这里是利用GCC的无定参数传递的写法

} native_handle_t;

/* keep the old definition for backward source-compatibility */

typedef native_handle_t native_handle;

typedef const native_handle* buffer_handle_t;

native_handle_t 是上层抽象的用于进程间传递的数据结构,对于 Gralloc 而言其内容就是:



data[0] 指向具体对象的内容,其中:

  static const int sNumInts = 8;

  static const int sNumFds = 1;

sNumFds=1表示有一个文件句柄:fd

sNumInts= 8表示后面跟了8个INT型的数据:magic,flags,size,offset,base,lockState,writeOwner,pid;

由于在上层系统不要关心buffer_handle_t中data的具体内容。在进程间传递buffer_handle_t(native_handle_t)

句柄是其实是将这个句柄内容传递到Client端。在客户端通过Binder读取readNativeHandle @Parcel.cpp新生成一个native_handle。

native_handle* Parcel::readNativeHandle() const

{

    int numFds, numInts;

    err = readInt32(&numFds);

    err = readInt32(&numInts);

    native_handle* h = native_handle_create(numFds, numInts);

    for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {

        h->data[i] = dup(readFileDescriptor());

        if (h->data[i] < 0) err = BAD_VALUE;

    }

    err = read(h->data + numFds, sizeof(int)*numInts);
...

}

这里构造客户端的native_handle时,对于fd进行dup处理(不同进程),其它的直接读取复制使用.

magic,flags,size,offset,base,lockState,writeOwner,pid 等复制到了客户端,从而为缓冲区共享获取到相应的信息

对于fd的写入binder特殊标志 BINDER_TYPE_FD:告诉Binder驱动这是一个fd描述符

status_t Parcel::writeFileDescriptor(int fd)

{

    flat_binder_object obj;

    obj.type = BINDER_TYPE_FD;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;

    obj.handle = fd;

    obj.cookie = (void*)0;

    return writeObject(obj, true);

}

2、GraphicBuffer 内存分配

三种分配方式:

  GraphicBuffer();

  // creates w * h buffer

  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage);

  // create a buffer from an existing handle

  GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,

          uint32_t stride, native_handle_t* handle, bool keepOwnership);

其实最终都是通过函数:initSize

status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,

        uint32_t reqUsage)

{

    if (format == PIXEL_FORMAT_RGBX_8888)

        format = PIXEL_FORMAT_RGBA_8888;

    GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();

    status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);

    if (err == NO_ERROR) {

        this->width  = w;

        this->height = h;

        this->format = format;

        this->usage  = reqUsage;

        mVStride = 0;

    }

    return err;

}

利用 GraphicBufferAllocator 类分配内存:
首先加载 libGralloc.hwXX.so 动态库,分配一块用于显示的内存,屏蔽掉不同硬件平台的区别。

GraphicBufferAllocator::GraphicBufferAllocator()

    : mAllocDev(0)

{

    hw_module_t const* module;

    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);

    LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID);

    if (err == 0) {

        gralloc_open(module, &mAllocDev);

    }

}

分配方式有两种:

status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,

      int usage, buffer_handle_t* handle, int32_t* stride)

{

    if (usage & GRALLOC_USAGE_HW_MASK) {

        err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);

    } else {

        err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride);

    }

    ...

}

具体的内存分配方式如下:



3、共享句柄的传递

frameworks\base\libs\surfaceflinger_client\ISurface.cpp

客户端请求处理:BpSurface 类:

  virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage)

  {

      Parcel data, reply;

      data.writeInterfaceToken(ISurface::getInterfaceDescriptor());

      data.writeInt32(bufferIdx);

      data.writeInt32(usage);

      remote()->transact(REQUEST_BUFFER, data, &reply);

      sp<GraphicBuffer> buffer = new GraphicBuffer();

      reply.read(*buffer);

      return buffer;

  }

  

 这里利用 sp<GraphicBuffer> buffer = new GraphicBuffer(); 然后reply.read(*buffer)将数据利用 unflatten反序化到这个buffer中并返回这个本地new出来的GraphicBuffer对象,而这个数据是在哪里写入进去的呢?

 

服务端呼应处理: BnSurface 类:

status_t BnSurface::onTransact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

    switch(code) {

        case REQUEST_BUFFER: {

            CHECK_INTERFACE(ISurface, data, reply);

            int bufferIdx = data.readInt32();

            int usage = data.readInt32();

            sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage));

            if (buffer == NULL)

                return BAD_VALUE;

            return reply->write(*buffer);

        }  

 

requestBuffer函数服务端调用流程:

requestBuffer @ surfaceflinger\Layer.cpp

sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)

{
buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage);
...
return buffer;

}

如此的话,客户端利用new 的 GraphicBuffer() 对象从 Parcel中读取 native_handle 对象及其内容,而在服务端由同样由 requestBuffer 请求返回一个真正的GraphicBuffer对象。那么这两个数据如何序列化传递的呢?

flatten @ GraphicBuffer.cpp

status_t GraphicBuffer::flatten(void* buffer, size_t size,

        int fds[], size_t count) const

{
...

    if (handle) {

        buf[6] = handle->numFds;

        buf[7] = handle->numInts;

        native_handle_t const* const h = handle;

        memcpy(fds,     h->data,             h->numFds*sizeof(int));

        memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int));

    }
flatten的职能就是将GraphicBuffer的handle变量信息写到Parcel句中,接收端利用unflatten读取

status_t GraphicBuffer::unflatten(void const* buffer, size_t size,

        int fds[], size_t count)

{

        native_handle* h = native_handle_create(numFds, numInts);

        memcpy(h->data,          fds,     numFds*sizeof(int));

        memcpy(h->data + numFds, &buf[8], numInts*sizeof(int));

        handle = h;

}

经过以上操作,在客户端构造了一个对等的 GraphicBuffer对象,下面将继续讲两者如何操作相同的内存块

4、共享内存的管理 -- Graphic Mapper 功能

两个进程间如何共享内存,如何获取到共享内存?Mapper就是干这个得。需要利用到两个信息:共享缓冲区设备句柄,分配时的偏移量.客户端需要操作一块共享内存时,首先利用 registerBuffer 注册一个 buffer_handle_t,然后利用lock函数获取缓冲区首地址进行绘图,即利用lock及unlock对内存进行映射使用。

利用lock(mmap)及unlock(ummap)进行一个缓冲区的映射。

重要的代码如下:mapper.cpp 

static int gralloc_map(gralloc_module_t const* module,

      buffer_handle_t handle,

      void** vaddr){

     private_handle_t* hnd = (private_handle_t*)handle;
void* mappedAddress = mmap(0, size,

              PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);

      if (mappedAddress == MAP_FAILED) {

          LOGE("Could not mmap %s", strerror(errno));

          return -errno;

      }

      hnd->base = intptr_t(mappedAddress) + hnd->offset;

      *vaddr = (void*)hnd->base;      

     return 0;

}

static int gralloc_unmap(gralloc_module_t const* module,

      buffer_handle_t handle){

      private_handle_t* hnd = (private_handle_t*)handle;

      void* base = (void*)hnd->base;

      size_t size = hnd->size;

      munmap(base, size);

      hnd->base = 0;

      return 0;

}

利用buffer_handle_t与private_handle_t句柄完成共享进程数据的共享:



总结:
Android在该节使用了共享内存的方式来管理与显示相关的缓冲区,他设计成了两层,上层是缓冲区管理的代理机构GraphicBuffer,

及其相关的native_buffer_t,下层是具体的缓冲区的分配管理及其缓冲区本身。上层的对象是可以在经常间通过Binder传递的,而在进程间并不是传递缓冲区本身,而是使用mmap来获取指向共同物理内存的映射地址。




13

0

上一篇Android
权限控制代码分析

下一篇android
-- NDK 编译环境搭建

我的同类文章

Android系统框架学习(101)

•sp&wp
的三板斧2015-11-09阅读429
•Android
待机功能流程分析2015-11-04阅读684
•bootchart
使用说明及代码分析2014-04-14阅读6964
•android
- home键及launcher启动流程分析2013-11-01阅读11856
•android
recovery 主系统代码分析2013-07-05阅读14444
•Android
无法接收开机广播的问题2015-11-06阅读1356
字符编码问题2015-11-04阅读387
sp和wp的实现过程~~2014-02-28阅读1062
•android
SystemUI 流程分析2013-10-18阅读15030
•android
-- 编译不同库及编译脚本之区别2013-03-18阅读2488
更多文章

猜你在找

2016软考系统集成项目管理工程师-上午历年真题解析培训视频课程
2016软考系统集成项目管理工程师视频教程精讲 基础知识(下)
2016软考系统集成项目管理工程师视频教程精讲 基础知识(上)
信息系统项目管理师高级考试视频辅导课程
Linux操作系统及常用命令实战和进阶

Android中的GraphicBuffer同步机制-Fence
Android中的GraphicBuffer同步机制Fence
Android中的GraphicBuffer同步机制-Fence
Android中的GraphicBuffer同步机制Fence
Android中的GraphicBuffer同步机制-Fence

<iframe id="iframeu1607657_0" src="http://pos.baidu.com/lckm?sz=728x90&rdid=1607657&dc=2&di=u1607657&dri=0&dis=0&dai=2&ps=10156x689&coa=at%3D3%26rsi0%3D728%26rsi1%3D90%26pat%3D6%26tn%3DbaiduCustNativeAD%26rss1%3D%2523FFFFFF%26conBW%3D1%26adp%3D1%26ptt%3D0%26titFF%3D%2525E5%2525BE%2525AE%2525E8%2525BD%2525AF%2525E9%25259B%252585%2525E9%2525BB%252591%26titFS%3D%26rss2%3D%2523000000%26titSU%3D0%26ptbg%3D90%26piw%3D0%26pih%3D0%26ptp%3D0&dcb=BAIDU_SSP_define&dtm=BAIDU_DUP_SETJSONADSLOT&dvi=0.0&dci=-1&dpt=none&tsr=0&tpr=1460298128731&ti=SurfaceFlinger%20GraphicBuffer%E5%86%85%E5%AD%98%E5%85%B1%E4%BA%AB%E7%BC%93%E5%86%B2%E5%8C%BA%E6%9C%BA%E5%88%B6%20-%20andyhuabing%E7%9A%84%E4%B8%93%E6%A0%8F%20-%20%E5%8D%9A%E5%AE%A2%E9%A2%91&ari=1&dbv=2&drs=3&pcs=1903x869&pss=1903x11683&cfv=0&cpl=4&chi=23&cce=true&cec=UTF-8&tlm=1460298128&ltu=http%3A%2F%2Fblog.csdn.net%2Fandyhuabing%2Farticle%2Fdetails%2F7489776&ltr=http%3A%2F%2Fblog.csdn.net%2Fandyhuabing%2Farticle%2Fcategory%2F843454%2F4&ecd=1&psr=1920x1080&par=1920x998&pis=-1x-1&ccd=24&cja=false&cmi=6&col=en-US&cdo=-1&tcn=1460298129&qn=5361b381e4e39a37&tt=1460298128535.280.1534.1536" width="728" height="90" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;"></iframe>

查看评论

3楼 CyberLogic 2014-09-29 11:11发表 [回复]


分析的不错,呵呵
2楼 goodtalent 2014-03-12 17:33发表 [回复]


看不懂啊。

Re: andyhuabing 2014-03-18 17:25发表 [回复]


回复goodtalent:多看看就懂了,呵呵
1楼 houyizi313 2013-04-12 10:39发表 [回复] [引用] [举报]


兄弟:你理解的台深奥了!我遇到了个问题!也需要研究下Android测显示机制!我想知道Android播放本地SD卡上面的视频,控制视频的显示大小的那一块源码在那个文件?那个部分的?因为刚开始入手,android层层封转,显示系统也是分层,我连这些层都分布清楚!所以难到了……非常感谢……

Re: andyhuabing 2014-03-18 17:24发表 [回复] [引用] [举报]


回复houyizi337825770:本地播放是使用JAVA类 : MediaPlayer.java

/**

* Sets the {@link SurfaceHolder} to use for displaying the video

* portion of the media.

*

* Either a surface holder or surface must be set if a display or video sink

* is needed. Not calling this method or {@link #setSurface(Surface)}

* when playing back a video will result in only the audio track being played.

* A null surface holder or surface will result in only the audio track being

* played.

*

* @param sh the SurfaceHolder to use for video display

*/

public static final int KEY_PARAMETER_VIDEO_POSITION_INFO = 2000;

public void setDisplay(SurfaceHolder sh) {

通过SurfaceView的大小指定视频大小的。其实就是将图形层挖了一个“洞”将视频透出来了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: