GraphicBuffer的使用(笔记)

关于GraphicBuffer的使用,前几天再查看一个内存泄漏的问题时,了解了一些关于Android上的GraphicBuffer使用的细节。以下的这个sample就是为了测试GraphicBuffer对应的gralloc如何分配内/释放内存,以及对于不同的线程或者是进程Gralloc如何处理这块GraphicBuffer的生命周期。以及不同的GraphicBuffer::HandleWrapMethod,对应的如何处理这个GraphicBuffer的构造和析构

1. GraphicBuffer的使用的sample 1:

对于初学者,可以参考frameworks/native/ui/tests的目录下的测试用例,写一些简单的GraphicBuffer的sample。

#include <ui/GraphicBuffer.h>
#include <utils/Thread.h>
#include <utils/Log.h>

using namespace android;

constexpr uint32_t kTestWidth = 1024;
constexpr uint32_t kTestHeight = 200;
constexpr uint32_t kTestLayerCount = 1;
constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
constexpr PixelFormat format = PIXEL_FORMAT_RGBA_8888;

//填充buffer的数据,以及参数设置
void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
    const size_t PIXEL_SIZE = 4;
    for (int x = 0; x < w; x++) {
        for (int y = 0; y < h; y++) {
            off_t offset = (y * stride + x) * PIXEL_SIZE;
            for (int c = 0; c < 4; c++) {
                int parityX = (x / (1 << (c+2))) & 1;
                int parityY = (y / (1 << (c+2))) & 1;
                buf[offset + c] = (parityX ^ parityY) ? 231 : 35;
            }
        }
    }
}

class MyThread : public Thread {
    Mutex mLock;
    sp<GraphicBuffer> buf;
    virtual bool threadLoop() {
        Mutex::Autolock l(mLock);
        usleep(1000);
        //uint8_t* img = nullptr;
        //buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
       // fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride());
        //buf->unlock();
        //buf.clear();
        return false;
    }

    public:
        explicit MyThread(const native_handle_t* inHandle)  {
            //buf = new GraphicBuffer(inHandle, GraphicBuffer::WRAP_HANDLE, kTestWidth, kTestHeight, format,
kTestLayerCount, kTestUsage, kTestWidth);
            buf = new GraphicBuffer(inHandle, GraphicBuffer::CLONE_HANDLE, kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage, kTestWidth);
            ALOGE("test MyThread create buffer successfully");
            buf->initCheck();
        }
        ~MyThread() {
            ALOGE("test ~MyThread successfully");
        }
};

int main (void) {
    sp<GraphicBuffer> buf(new GraphicBuffer(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage,std::string("test")));
    buf->initCheck();
    uint8_t* img = nullptr;
    buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
    MyThread* th = new MyThread(buf->getNativeBuffer()->handle);
    th->run("Ruilitest");
    fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride());
    buf->unlock();
}

说明: 以上的sample,大致的意思是,在主线程先创建一个GraphicBuffer,然后把这个native_handle_t传递给MyThread,这个MyThread会创建一个新的GraphicBuffer,那这些GraphicBuffer的构造函数中有一个关键的参数GraphicBuffer::HandleWrapMethod.

2. 关于参数GraphicBuffer::HandleWrapMethod的说明

头文件GraphicBuffer.h中说明如下:
enum HandleWrapMethod : uint8_t {
    // Wrap and use the handle directly.  It assumes the handle has been
    // registered and never fails.  The handle must have a longer lifetime
    // than this wrapping GraphicBuffer.
    //
    // This can be used when, for example, you want to wrap a handle that
    // is already managed by another GraphicBuffer.
    WRAP_HANDLE,

    // Take ownership of the handle and use it directly.  It assumes the
    // handle has been registered and never fails.
    //
    // This can be used to manage an already registered handle with
    // GraphicBuffer.
    TAKE_HANDLE,

    // Take onwership of an unregistered handle and use it directly.  It
    // can fail when the buffer does not register.  There is no ownership
    // transfer on failures.
    //
    // This can be used to, for example, create a GraphicBuffer from a
    // handle returned by Parcel::readNativeHandle.
    TAKE_UNREGISTERED_HANDLE,

    // Make a clone of the handle and use the cloned handle.  It can fail
    // when cloning fails or when the buffer does not register.  There is
    // never ownership transfer.
    //
    // This can be used to create a GraphicBuffer from a handle that
    // cannot be used directly, such as one from hidl_handle.
    CLONE_HANDLE,
};

2.2 CLONE_HANDLE

这个method在整个Android的工程中,用的比较多,因为这个比较安全,但是这个也是需要按照一定的规则使用,因为上层调用这个在使用CLONE_HANDLE创建出来的GraphicBuffer。

比如说有些gpu的芯片方实现driver的时候,要求上层要保证gpu使用的时候这个GraphicBuffer的生命周期有效,这个buffer的引用计数不能减到0,因为如果上层还有在用这个handle的时候,gpu释放,根据不同的厂家gpu的实现,出现不同的问题。有些gpu释放GraphicBuffer的时候,对应的handle被lock的话,有些gpu的driver是实现不会处理这个error的信息,只抛一个warning,最种可能导致gralloc上内存泄漏。

接着说CLONE_HANDLE的作用,这个method的使用,是为了保证,进程或者是线程之间传递这个native_handle_t的时候,防止这个handle对应的实际物理内存被别的进程或者线程释放掉,需要调用

native_handle_create 创建一个新的native_handle_t,对这个物理buffer引用+1

其实也是调用了dup的方法,复制出来一个这个handle对应的fd,就是在kernel那边注册下,告诉kernel或者是实际物理内存管理者,这个内存我还在用,需要把这个buffer的引用计数+1,不能被释放。等我不用的时候,把这个

native_handle_close
native_handle_delete的时候,再把这个buffer的引用计数-1

注:那这块实际的buffer释放,是不确定的,谁最后调用了引用计数-1,正好踩到计数0的话,就可能会真的析构这个物理buffer。

2.1 WRAP_HANDLE

这个method使用需要保证严格的同步, 比如说:
App创建了A handle -> 传递给Hal的线程使用 –> 再返回给App使用
这三个步骤严格统一的话,是可以使用这个method的。
这个method的,不需要重新创建对应的native_handle_t相关的数据结构,也不用去做

native_handle_create –>
native_handle_close((native_handle_t *)psNativeHandle) ->
native_handle_delete((native_handle_t *)psNativeHandle)

因为GraphicBuffer的构造和析构会根据method,决定是否去创建新的native_handle_t,WRAP_HANDLE是表示,只使用这个native_handle_t,不管理这个native_handle_t对应的内存释放。

3. GraphicBuffer的使用的sample 2:

Sample2 简化为三步:

	第一步:sp<GraphicBuffer> test1 = new GraphicBuffer()  -> 创建GraphicBuffer,应该是调用下面的模板类生成sp的指针
sp的模板类如下:
template<typename T>
sp<T>& sp<T>::operator =(T* other) {
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    if (other) other->incStrong(this); // 这边调用这个GraphicBuffer的引用+1
    if (oldPtr) oldPtr->decStrong(this);
    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();//如果在这个sp的类外面有多线程的访问的话,有可能会有另外的线程也调用了赋值的这个函数,会把oldPtr给修改掉这样就有race的问题,因为sp没有在赋值的地方做锁的保护,所以需要在用的地方做线程的同步。
    m_ptr = other;
    return *this;
}
   第二步: test1.clear()的调用如下:
  template<typename T>
  void sp<T>::clear() {
      T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
      if (oldPtr) {
          oldPtr->decStrong(this); //在这边会有引用计数-1的操作
          if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();//同样也会有冲突的check
          m_ptr = nullptr;
      }
  }
   第三步:test1 = nullptr的话,跟第一个sp指针赋值的过程相同,同样也会调用引用计数-1

总之:谁踩到了减到0 的点,谁就会接着调用GraphicBuffer的析构,这个跟物理buffer的析构不是一个引用,因为物理buffer由驱动或者kernel管理生命周期。

4. GraphicBuffer::getNativeBuffer()的函数说明

ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const
{
    return static_cast<ANativeWindowBuffer*>(const_cast<GraphicBuffer*>(this));
}

打印GraphicBuffer的指针和ANativeWindowBuffer的指针,但是返回的地址不是相同的,会有一定的偏移。

GraphicBuffer=0xef9701e0, ANativeWindowBuffer= 0xef9701e8

这个应该是c++中类继承的时候强转的计算,要做偏移之后才能指向AANativeWindowBuffer

5. GraphicBuffer::free_handle()函数说明

void GraphicBuffer::free_handle()
{
    ALOGE("GFX GraphicBuffer::free_handle() this = %p, handle = %p", this, handle);
    if (mOwner == ownHandle) { // 对应CLONE_HANDLE的流程
        mBufferMapper.freeBuffer(handle);
    } else if (mOwner == ownData) {//这个看code里面没有调用的地方
        GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
        allocator.free(handle); //这边也是会调用到mBufferMapper.freeBuffer(handle)
    }
    handle = nullptr;
}

6. GraphicBuffer::lock / GraphicBuffer::unlock

读写这个buffer之前需要先lock这个buffer,map出来一个虚拟地址,写完或者是读完之后,需要unlock释放。
lock和unlock的使用是需要配对,否则会有虚拟内存异常的问题或者是gpu的driver异常。

总结
GraphicBuffer是可以进程间共享的buffer,进程间传递的时候,除了一些GraphicBuffer中宽高参数传递之外,还有一个重要的参数,就是这个buffer对应的fd。GraphicBuffer对应的物理buffer,在kernel中有对应的管理,也就是物理buffer的引用计数,对于上层不同的应用,每个应用都可以用一个fd对应这个物理buffer,这个fd在不同的进程中也不一定相同。上面说的native_handle_create 就是生成一个新的fd,也就是对这个物理buffer的引用计数+1。(这个是从别的博客和跟同事讨论中获取的一些信息,可能也表述的不够精确,但是fd大概的意思就这样,嘿嘿)

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值