背景介绍:
Android Camera系统经常需要一块空间来存放图像数据。从连续性来说内存有连续和非连续之分,从内存的来源来说内存可以从系统分配也可以从设备分配,从V4L2框架向用户空间暴露内存的类型来说有MMAP、USERPTR、DMABUF三种。
Android12之前Camera HAL用来存放图像的buffer通常是通过ION分配的,Android12 GKI 2.0将ION分配器替换为DMA-BUF heap。
接下来,主要介绍如何使用DMA-BUF heap模块的接口分配buffer,用于Camera HAL图像处理。
内容介绍:
DMA-BUF heap内存分配/释放接口
DMA-BUF heap内存分配器的实现
DMA-BUF heap使用的例子
- DMA-BUF heap内存分配/释放接口
- DMA-BUF heap内存分配器的实现
- DMA-BUF heap使用的例子
DMA-BUF heap内存分配/释放接口
Android14 BufferAllocator是对DMA-BUF heap分配器的抽象, 通过BufferAllocator类可以从系统dma heap分配一块buffer,buffer可以是连续的也可以是不连续的。通过heap名子区分这块buffer是连续的还是不连续的,是不连续的buffer是带cache还是不带cache。/dev/dma_heap路径下的节点名即heap名:
- linux,cma 连续内存
- system 非连续内存
- system-uncached 非连续不带cache
BufferAllocator提供的接口可以分为三个部分:
- 分配器相关的接口
//获取系统提供的DMA-BUF heap列表。即/dev/dma_heap路径下的节点
static std::unordered_set<std::string> GetDmabufHeapList()
//给dmabuf_fd对应的buffer设置一个名字
static int DmabufSetName(unsigned int dmabuf_fd, const std::string& name)//创建一个BufferAllocator对象,后续buffer通过这个分配器分配。BufferAllocator对象不支持copy和move操作。
BufferAllocator()
- Buffer分配相关的接口
//从指定名字的heap分配,返回dmabuf_fd. heap_name对应/dev/dma_heap路径下的文件名
int Alloc(const std::string& heap_name, size_t len, unsigned int heap_flags = 0,size_t legacy_align = 0)
//从system heap (cached/uncached)分配,并返回dmabuf fd
int AllocSystem(bool cpu_access, size_t len, unsigned int heap_flags = 0,size_t legacy_align = 0);
- cache一致性相关接口
//CPU访问分配的这块内存前必需调用CpuSyncStart,
int CpuSyncStart(unsigned int dmabuf_fd, SyncType sync_type = kSyncRead,const CustomCpuSyncLegacyIon& legacy_ion_cpu_sync = nullptr,void *legacy_ion_custom_data = nullptr)
//一旦CPU完成对这块内存的访问,必需调用CpuSyncEnd
int CpuSyncEnd(unsigned int dmabuf_fd, SyncType sync_type = kSyncRead,const CustomCpuSyncLegacyIon& legacy_ion_cpu_sync = nullptr,void* legacy_ion_custom_data = nullptr);
DMA-BUF heap内存分配器的实现
DMA-BUF heap内存分配器实现是直接操作节点。系统将一块空间通过设备节点的方式暴露给用户空间,BufferAllocator中通过这些暴露的设备节点完成buffer的分配释放,以及一致性操作。
BufferAllocator实现有一个重要的对象属性dmabuf_heap_fds_用于记录打开的dmabuf_heap的句柄, 定义:
std::unordered_map<std::string, android::bases::unique_fd> dmabuf_heap_fds_
当调用Alloc()从DMA-BUF heap分配一块空间时,BufferAllocator首先会从dmabuf_heap_fds_表查找这个heap节点是不是已经打开。第一次从这个heap分配空间时会先打开这个heap节点,即/dev/dmabuf_heap/路径下的设备节点,得到文件句柄,并记录到dmabuf_heap_fds, 后续再从这个heap分配的时候就可以直接调用系统ioctl()从核态分配一块buffer。
BufferAllocator的结构比较简单:
DMA-BUF heap使用的例子
以下代码实现一个内存分配器,分配一块空间送驱动获取图像。
#define IMAGE_MAX_PLANES 2struct ImagePlane
{unsigned int bytesused;unsigned int length;unsigned int mem_offset;void* userptr;unsigned int dma_fd;unsigned int data_offset;
};struct ImageBuffer
{unsigned int fourcc; //image fourcc, eg: V4L2_PIX_FMT_NV12unsigned int width; //image widthunsigned int height; //image heightunsigned int np; //number of planesunsigned int state; //revert for import bufferstruct ImagePlane planes[IMAGE_MAX_PLANES];
};class Allocator
{
private:static Allocator instance;std::shared_ptr<BufferAllocator> alloc;Allocator() { alloc = std::make_shared<BufferAllocator>(); }public:Allocator(const Allocator&) = delete;Allocator& operator=(const Allocator&) = delete;static Allocator& getInstance() { return instance; }BufferAllocator* GetAllocator() { return alloc.get(); }int AllocateBuffer(struct ImageBuffer* frame);int ReleaseBuffer(struct ImageBuffer* frame);
};
int Allocator::AllocateBuffer(struct ImageBuffer* frame)
{int i = 0;if (frame == NULL)return -1;for (i = 0; i < static_cast<int>(frame->np); i++) {frame->planes[i].dma_fd = alloc->Alloc("system", frame->planes[i].bytesused);if (frame->planes[i].dma_fd < 0) {ALOGE("Allocate buffer failed.");goto alloc_fail;}frame->planes[i].userptr = (unsigned char*)mmap(NULL, frame->planes[i].bytesused,PROT_READ|PROT_WRITE, MAP_SHARED,frame->planes[i].dma_fd, 0);if (frame->planes[i].userptr == MAP_FAILED) {ALOGE("Map buffer failed.");goto map_fail;}ALOGD("frame planes[%d] dma_fd %d userptr %p", i, frame->planes[i].dam_fd,frame->planes[i].userptr);}return 0;map_fail:close(frame->planes[i].dma_fd);
alloc_fail:for (int k = 0; k < i; k++) {munmap(frame->planes[k].userptr, frame->planes[k].bytesused);close(frame->planes[i].dma_fd);}return -1;
}int Allocator::ReleasesBuffer(struct ImageBuffer* frame)
{int i = 0;if (frame == NULL)return -1;for (i = 0; i < static_cast<int>(frame->np); i++) {munmap(frame->planes[i].userptr, frame->planes[i].bytesused);close(frame->planes[i].dma_fd);}return 0;
}
Allocator Allocator::instance;