在上一篇文章中介绍 GraphicBuffer 初始化的 initWithSize() 函数中,有两个核心方法,GraphicBufferAllocator.allocate 和 GraphicBufferMapper. getTransportSize,这里我们就来看一下 allocate() 函数是如何实现内存申请的。
一、函数介绍
在Android的图形子系统中,GraphicBufferAllocator 和 GraphicBufferMapper 是处理图形缓冲区的核心组件。这两个类分别负责缓冲区的分配和映射,是 GraphicBuffer 类的基础,GraphicBuffer 用于封装和管理图形数据。
GraphicBufferAllocator.allocate
GraphicBufferAllocator 的 allocate 方法用于从系统中申请一块内存,这块内存将被用作图形缓冲区。这个方法通常在需要创建一个新的图形缓冲区时调用,例如,当一个应用程序需要显示一帧图像或视频帧时。
通常,GraphicBufferAllocator.allocate 会在创建新的 GraphicBuffer 时调用。
二、内存申请
1、GraphicBufferAllocator.cpp
源码位置:/frameworks/native/libs/ui/GraphicBufferAllocator.cpp
allocate
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
uint64_t /*graphicBufferId*/, std::string requestorName) {
return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName, true);
}
status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
std::string requestorName, bool importBuffer) {
ATRACE_CALL();
// 确保缓冲区宽度和高度至少为1
if (!width || !height)
width = height = 1;
……
// 确保layerCount至少为1
if (layerCount < 1) {
layerCount = 1;
}
// 清除usage标志中无效的位
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
// 调用底层分配器,实际分配内存
status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
1, stride, handle, importBuffer);
……
// 如果importBuffer参数为真,表示正在导入一个现有的缓冲区,而非创建新的
if (!importBuffer) {
return NO_ERROR;
}
size_t bufSize;
// 计算缓冲区的大小,如果stride(行距)不合理,则使用输入宽度计算大小
if ((*stride) != 0 && std::numeric_limits<size_t>::max() / height / (*stride) < static_cast<size_t>(bpp)) {
bufSize = static_cast<size_t>(width) * height * bpp;
} else {
bufSize = static_cast<size_t>((*stride)) * height * bpp;
}
Mutex::Autolock _l(sLock);
// 将分配记录添加到sAllocList中
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
alloc_rec_t rec;
rec.width = width;
rec.height = height;
rec.stride = *stride;
rec.format = format;
rec.layerCount = layerCount;
rec.usage = usage;
rec.size = bufSize;
rec.requestorName = std::move(requestorName);
list.add(*handle, rec);
return NO_ERROR;
}
该函数主要使用 Gralloc4Allocator 进行图形缓冲区的分配,同时也处理了一些边界条件和错误情况,确保分配过程的健壮性和安全性。此外,对于导入缓冲区的情况,还进行了额外的管理,以便于后续的跟踪和释放。
2、Gralloc4.cpp
源码位置:/frameworks/native/libs/ui/Gralloc4.cpp
status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
// 获取缓冲区的描述符信息
if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, &descriptorInfo) != OK) {
return error;
}
BufferDescriptor descriptor;
// 创建一个BufferDescriptor实例
status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo), static_cast<void*>(&descriptor));
if (error != NO_ERROR) {
return error;
}
// 根据是否有mAidlAllocator(AIDL接口),选择不同的分配方式
if (mAidlAllocator) {
AllocationResult result;
// 分配指定数量的缓冲区
auto status = mAidlAllocator->allocate(descriptor, bufferCount, &result);
if (!status.isOk()) {
……
} else {
if (importBuffers) {
for (uint32_t i = 0; i < bufferCount; i++) {
auto handle = makeFromAidl(result.buffers[i]);
// 导入每个缓冲区
error = mMapper.importBuffer(handle, &outBufferHandles[i]);
native_handle_delete(handle);
……
}
} else {
for (uint32_t i = 0; i < bufferCount; i++) {
// 复制缓冲区句柄
outBufferHandles[i] = dupFromAidl(result.buffers[i]);
if (!outBufferHandles[i]) {
for (uint32_t j = 0; j < i; j++) {
auto buffer = const_cast<native_handle_t*>(outBufferHandles[j]);
native_handle_close(buffer);
native_handle_delete(buffer);
outBufferHandles[j] = nullptr;
}
}
}
}
}
*outStride = result.stride;
// Release all the resources held by AllocationResult (specifically any remaining FDs)
result = {};
// make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
hardware::IPCThreadState::self()->flushCommands();
return error;
}
// mAidlAllocator不存在时
auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError,
const auto& tmpStride, const auto& tmpBuffers) {
……
if (importBuffers) {
for (uint32_t i = 0; i < bufferCount; i++) {
error = mMapper.importBuffer(tmpBuffers[i], &outBufferHandles[i]);
……
}
} else {
for (uint32_t i = 0; i < bufferCount; i++) {
outBufferHandles[i] = native_handle_clone(tmpBuffers[i].getNativeHandle());
if (!outBufferHandles[i]) {
for (uint32_t j = 0; j < i; j++) {
auto buffer = const_cast<native_handle_t*>(outBufferHandles[j]);
native_handle_close(buffer);
native_handle_delete(buffer);
outBufferHandles[j] = nullptr;
}
}
}
}
*outStride = tmpStride;
});
// 确保内核驱动程序现在看到BC_FREE_BUFFER并关闭fds
hardware::IPCThreadState::self()->flushCommands();
return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
}
这里主要利用 Gralloc4Mapper 和可能的 AIDL 接口来分配图形缓冲区,最终都是调用 Hal 层的 allocate() 方法申请多个缓冲区。在申请完内存之后,将会调用 GraphicBufferMapper 的 importBuffer() 方法,让这段句柄对应的内存变得可用。
3、GrallocAllocator.cpp
源码位置:/hardware/google/gchips/GrallocHAL/src/4.x/GrallocAllocator.cpp
Return<void> GrallocAllocator::allocate(const BufferDescriptor &descriptor, uint32_t count, allocate_cb hidl_cb)
{
MALI_GRALLOC_LOGV("Allocation request from process: %lu", callingPid());
// 将HIDL描述符转换为原生格式
buffer_descriptor_t bufferDescriptor;
if (!mapper::common::grallocDecodeBufferDescriptor(descriptor, bufferDescriptor))
{
hidl_cb(Error::BAD_DESCRIPTOR, 0, hidl_vec<hidl_handle>());
return Void();
}
// 调用通用分配函数
common::allocate(bufferDescriptor, count, hidl_cb);
return Void();
}
这里最终调用通用分配函数 common::allocate() 进行实际的内存分配。
4、Allocator.cpp
源码位置:/hardware/google/gchips/GrallocHAL/src/hidl_common/Allocator.cpp
void allocate(const buffer_descriptor_t &bufferDescriptor, uint32_t count, IAllocator::allocate_cb hidl_cb,
std::function<int(const buffer_descriptor_t *, buffer_handle_t *)> fb_allocator)
{
……
// 循环分配缓冲区
for (uint32_t i = 0; i < count; i++)
{
buffer_handle_t tmpBuffer = nullptr;
int allocResult;
……
{
// 分配缓冲区
allocResult = mali_gralloc_buffer_allocate(grallocBufferDescriptor, 1, &tmpBuffer, nullptr);
……
// 分配属性区域内存,并初始化元数据。
mali_gralloc_ion_allocate_attr(hnd);
hnd->attr_base = mmap(nullptr, hnd->attr_size, PROT_READ | PROT_WRITE, MAP_SHARED, hnd->get_share_attr_fd(), 0);
……
}
……
}
这里主要根据提供的缓冲区描述符和数量来分配一组缓冲区,并调用 mali_gralloc_buffer_allocate 进行内存分配。
5、mali_gralloc_bufferallocation.cpp
源码位置:/hardware/google/gchips/GrallocHAL/src/core/mali_gralloc_bufferallocation.cpp
int mali_gralloc_buffer_allocate(const gralloc_buffer_descriptor_t *descriptors,
uint32_t numDescriptors, buffer_handle_t *pHandle, bool *shared_backend, int fd)
{
……
// 遍历描述符并更新使用标志
for (uint32_t i = 0; i < numDescriptors; i++)
{
……
}
/* 分配后备存储内存 */
err = mali_gralloc_ion_allocate(descriptors, numDescriptors, pHandle, &shared, fd);
……
// 更新句柄信息
for (uint32_t i = 0; i < numDescriptors; i++)
{
……
}
……
return 0;
}
6、mali_gralloc_ion.cpp
源码位置:/hardware/google/gchips/GrallocHAL/src/allocator/mali_gralloc_ion.cpp
mali_gralloc_ion_allocate_attr
int mali_gralloc_ion_allocate_attr(private_handle_t *hnd)
{
// 获取ION设备实例dev
ion_device *dev = ion_device::get();
……
// 从ION堆中分配内存
hnd->fds[idx] = dev->alloc_from_ion_heap(usage, hnd->attr_size, ion_flags, &min_pgsz);
……
// 将分配得到的文件描述符存储在hnd->fds[idx]中
hnd->incr_numfds(1);
return 0;
}
该函数主要用于为 Mali GPU 图形分配器中的 private_handle_t 类型的句柄分配属性区域的 ION 内存。
alloc_from_ion_heap
int ion_device::alloc_from_ion_heap(uint64_t usage, size_t size, unsigned int flags, int *min_pgsz)
{
……
// 根据usage参数选择合适的ION堆掩码。确定从哪个ION堆进行内存分配
unsigned int heap_mask = select_heap_mask(usage);
int shared_fd;
auto dmabuf_heap_name = select_dmabuf_heap(heap_mask);
// 从DMABUF堆中选择一个堆名称
if (!dmabuf_heap_name.empty()) {
// 进行DMABUF堆内存分配
shared_fd = alloc_from_dmabuf_heap(dmabuf_heap_name, size, flags);
} else {
……
// 使用传统方法进行ION堆内存分配
shared_fd = exynos_ion_alloc(ion_client, size, heap_mask, flags);
}
// 设置最小页大小
*min_pgsz = SZ_4K;
return shared_fd;
}
该函数提供了从 ION 堆中分配内存的能力,支持通过 DMABUF 堆或传统 ION 堆进行分配,确保了系统可以根据不同的需求和可用资源选择最合适的内存分配策略。
alloc_from_dmabuf_heap
std::unique_ptr<BufferAllocator> buffer_allocator;
int ion_device::alloc_from_dmabuf_heap(const std::string& heap_name, size_t size, unsigned int flags)
{
……
int shared_fd = buffer_allocator->Alloc(heap_name, size, flags);
……
return shared_fd;
}
这里是从 DMABUF 堆分配内存的关键接口,通过 BufferAllocator 对象提供了灵活且高效的内存分配能力。
7、BufferAllocator.cpp
源码位置:/system/memory/libdmabufheap/BufferAllocator.cpp
int BufferAllocator::Alloc(const std::string& heap_name, size_t len,
unsigned int heap_flags, size_t legacy_align) {
int fd = DmabufAlloc(heap_name, len);
if (fd < 0)
fd = IonAlloc(heap_name, len, heap_flags, legacy_align);
return fd;
}
这里首先尝试使用 DmabufAlloc 从指定的 DMABUF 堆中分配内存。如果 DmabufAlloc 分配失败(即返回值小于 0),则会回退到使用 IonAlloc从ION 堆中分配内存。
DmabufAlloc
int BufferAllocator::DmabufAlloc(const std::string& heap_name, size_t len) {
// 打开指定的DMABUF堆
int fd = OpenDmabufHeap(heap_name);
if (fd < 0) return fd;
// 准备分配数据结构
struct dma_heap_allocation_data heap_data{
.len = len, // 要分配的数据长度,以字节为单位
.fd_flags = O_RDWR | O_CLOEXEC, // 要分配的内存权限
};
// 通过系统调用向内核发送分配请求
auto ret = TEMP_FAILURE_RETRY(ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &heap_data));
……
return heap_data.fd;
}
可以看到这里最终通过 ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &heap_data) 向内核发送 DMA_HEAP_IOCTL_ALLOC 命令,实现从 DMABUF 堆中分配内存。