[Android禅修之路] 解读 GraphicBuffer 之 Framework 层
一 前言
GraphicBuffer 是 SurfaceFlinger 中一块重要的内容, 它涉及到了我们应用程序的数据是如何和SurfaceFlinger进行传递的。
在介绍 GraphicBuffer 之前,我们先提出这样一个问题:我们应用程序的界面数据,是如何传递给 SurfaceFlinger 进行合成和显示的。是 Binder 吗?显然不是,Binder 传递不了这么大的数据。那么是共享内存吗,早期的界面数据的确是通过这种方式传递的,但是那已经是很早之前了。
前面我们介绍了SurfaceFlinger中的生产者和消费者模型, 在生产者申请 buffer 的时候, 如果拿到的 Slot 没有和 GraphicBuffer 进行绑定, 那么就会先创建一个 GraphicBuffer , 然后进行绑定
从这一篇开始,我们就来探究 GraphicBuffer 的工作原理。
1.1 GraphicBuffer 的创建
在前面介绍 dequeueBuffer 申请缓冲区的时候, 我们还说了一种逻辑, 那就是从 BufferQueue 中拿到的 slot 没有关联的 GraphicBuffer , 那么这种情况下还需要单独创建 GraphicBuffer ,这里我们就来看看 GraphicBuffer 的创建逻辑
[frameworks/native/libs/gui/BufferQueueProducer.cpp]
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
// 如果拿到的缓冲区的 flag 为 BUFFER_NEEDS_REALLOCATION ,就创建一个 GraphicBuffer
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
}
在之前解读 BufferQueue的时候介绍了 BufferQueue 中的生产者和消费者,以及最重要的四个函数,其中的生产者在生成内容的时候,就会有这么一个过程;
- 生产者向 BufferQueue 中申请 slot(缓冲槽)
- 生产者拿到 slot,但是 slot 并没有关联对应的 GraphicBuffer(缓冲区)
- 生产者创建一个缓冲区,并将它与缓冲槽相关联。
如上代码,就是步骤2中的一个片段。接下来,我们看 GraphicBuffer 创建时的具体流程。
1.2 GraphicBuffer的构造函数
[frameworks/native/libs/ui/GraphicBuffer.cpp]
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inUsage, std::string requestorName)
: GraphicBuffer(inWidth, inHeight, inFormat, 1, static_cast<uint64_t>(inUsage), requestorName)
{
}
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inLayerCount, uint64_t inUsage, std::string requestorName)
: GraphicBuffer() {
mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
std::move(requestorName));
}
GraphicBuffer 的构造函数非常简单, 它只是调用了一个初始化函数 initWithSize。
1.3 GraphicBuffer::initWithSize
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
std::string requestorName) {
// 获取一个 GraphicBufferAllocator 对象, 这个对象是一个单例
// GraphicBufferAllocator 主要负责 GraphicBuffer 的内存分配
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
uint32_t outStride = 0;
// 分配一块制定宽高的 GraphicBuffer
status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
inUsage, &handle, &outStride, mId,
std::move(requestorName));
if (err == NO_ERROR) {
// 通过 GraphicBufferMapper 将这块 GraphicBuffer 的参数记录下来
// GraphicBufferMapper 负责的是 GraphicBuffer 的内存映射
mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
// 初始化参数
width = static_cast<int>(inWidth);
height = static_cast<int>(inHeight);
format = inFormat;
layerCount = inLayerCount;
usage = inUsage;
usage_deprecated = int(usage);
stride = static_cast<int>(outStride);
}
return err;
}
这里的初始化函数中,创建了一个 GraphicBufferAllocator 对象,这个我们申请的 GraphicBuffer 内存,其实是通过 GraphicBufferAllocator 这个对象进行分配的。
从这里开始,就需要注意了,因为在创建 GraphicBuffer 并分配内存的时候,会通过一个个对象不断的调用,从 Framework 层一直到最后的硬件层。
首先我们来看 GraphicBufferAllocator 这个类。
二 GraphicBufferAllocator
GraphicBufferAllocator 它是一个单例,外部使用时可以通过它来为 GraphicBuffer 来分配内存,Android 系统是为了屏蔽不同硬件平台的差异性,所以使用它来为外部提供一个统一的接口。
2.1 GraphicBufferAllocator::allocate
GraphicBufferAllocator::allocate 函数就是分配内存的具体函数, 它又通过调用一个 mAllocator 对象的 allocate 来分配内存, 这个 mAllocator 是一个 GrallocAllocator 的指针对象。GrallocAllocator 定义在frameworks/native/libs/ui/include/ui/Gralloc.h
,它有几个实现分别是 定义在 Gralloc3.h 中的 Gralloc3Allocator , 和定义在 Gralloc2.h 中的 Gralloc2Allocator
[frameworks/native/libs/ui/GraphicBufferAllocator.cpp]
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) {
// 如果宽或者高为0, 则将宽高设置为1
if (!width || !height)
width = height = 1;
// 如果图层的数量少于1, 则将图层的数量设置为1
if (layerCount < 1)
layerCount = 1;
// 移除调用者中的无效位
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
// 分配内存,使用的是 GrallocAllocator 指针,根据不同的版本有哦不同的实现,
// 这里我们假设它的实现是 Gralloc3Allocator
status_t error =
mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
if (error == NO_ERROR) {
// 初始化参数
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint32_t bpp = bytesPerPixel(format);
alloc_rec_t rec;
rec.width = width;
rec.height = height;
rec.stride = *stride;
rec.format = format;
rec.layerCount = layerCount;
rec.usage = usage;
rec.size = static_cast<size_t>(height * (*stride) * bpp);
rec.requestorName = std::move(requestorName);
list.add(*handle, rec);
return NO_ERROR;
} else {
return NO_MEMORY;
}
}
GraphicBufferAllocator 有两个实现类, 我们来看它其中的一个实现类 Gralloc3Allocator
2.2 Gralloc3Allocator
GrallocAllocator 有多个实现版本,Gralloc3Allocator 就是实现 Gralloc3 的版本
2.2.1 Gralloc3Allocator的定义
在 Gralloc3Allocator 定义的构造函数中,只有一个参数,就是 Gralloc3Mapper。这个参数在后面做内存分配的时候会用上,接着来看它构造函数具体的实现
[frameworks/native/libs/ui/include/ui/Gralloc3.h]
class Gralloc3Allocator : public GrallocAllocator {
public:
// Gralloc3Allocator 的构造函数需要传递一个 Gralloc3Mapper,因为分配内存依赖映射器 mapper
Gralloc3Allocator(const Gralloc3Mapper& mapper);
bool isLoaded() const override;
std::string dumpDebugInfo() const override;
// 它只有一个函数,就是分配内存
status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
buffer_handle_t* outBufferHandles) const override;
private:
const Gralloc3Mapper& mMapper;
// 真正实现分配内存的则是 mAllocator,这是通过 HAL 调用。
// HAL 层调用使用的通信方式就是 HIDL,其实和我们使用的 AIDL 是一样的原理。
sp<hardware::graphics::allocator::V3_0::IAllocator> mAllocator;
};
2.2.2 Gralloc3Allocator的构造函数
[frameworks/native/libs/ui/Gralloc3.cpp]
Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
return;
}
}
Gralloc3Allocator 的构造函数会传递一个 Gralloc3Mapper 作为参数, 并将它赋值给 mMapper , 当然, 其中最关键的还是通过一个 IAllocator::getService() 获取到了一个 mAllocator 对象, 这个 mAllocator 对象就是 hardware::graphics::allocator::V3_0::IAllocator 的指针。
看到这里,我们大概就能猜到,这和我们常见的进程间获取服务的方式很相似,在我们在应用中使用系统服务时,也是先通过 getService 拿到注册好的服务,然后再通过这个 Bp 对象,调用对应的服务函数。
2.2.3 Gralloc3Allocator的allocate
然后我们看 Gralloc3Allocator 分配内存的函数
status_t Gralloc3Allocator::allocate(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) const {
// 定义一个缓冲区的描述信息 descriptorInfo , 并将缓冲区的相关参数都封装到这个描述信息 descriptorInfo 中
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
BufferDescriptor descriptor;
// 通过之前构造函数中的 mMapper 对象来创建一个描述信息
status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
static_cast<void*>(&descriptor));
if (error != NO_ERROR) {
return error;
}
// 通过 mAllocator 这个 Bp 对象来调用对应的系统服务
// 调用到 hardware::graphics::allocator::V3_0::IAllocator 的 allocate 函数
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
error = static_cast<status_t>(tmpError);
if (tmpError != Error::NONE) {
return;
}
// import buffers
for (uint32_t i = 0; i < bufferCount; i++) {
error = mMapper.importBuffer(tmpBuffers[i],
&outBufferHandles[i]);
if (error != NO_ERROR) {
for (uint32_t j = 0; j < i; j++) {
mMapper.freeBuffer(outBufferHandles[j]);
outBufferHandles[j] = nullptr;
}
return;
}
}
*outStride = tmpStride;
});
// 确保内核驱动程序看到 BC_FREE_BUFFER 并立即关闭fds
hardware::IPCThreadState::self()->flushCommands();
return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
}
Gralloc3Allocator 的内存分配主要用到了两个对象,分别是继承自 Gralloc::GraphicBufferMapper
的 mMapper,和继承自 GraphicBufferAllocator
的 mAllocator。
mAllocator 是通过 HAL 创建的对象, 这个 IAllocator 是一个 HIDL 接口, HIDL 其实就是 hardware 层的 AIDL , 它和我们应用层的 AIDL 其实是一样的, 在我们应用层, 如果想使用 AIDL 进行跨进程通信, 需要定义一个 aidl 后缀名的文件, 而在系统的 HAL 层, 如果想使用 HIDL 进行跨进行通信也是类似, 需要定义一个 hal 后缀名的文件.。
例如 IAllocator 这个接口,其实就定义中 hardware/interfaces/graphics/allocator/3.0/ 路径下的 IAllocator.hal 中
[hardware/interfaces/graphics/allocator/3.0/IAllocator.hal]
package android.hardware.graphics.allocator@3.0;
import android.hardware.graphics.mapper@3.0;
// 这是一个接口
interface IAllocator {
// 调试相关的方法,不关心
dumpDebugInfo() generates (string debugInfo);
// 按照描述符的属性分配缓冲区
allocate(BufferDescriptor descriptor, uint32_t count)
generates (Error error,
uint32_t stride,
vec<handle> buffers);
};
IAllocator.hal 定义的方法很简单, 它只有一个 allocate 函数,这个就是我们用来分配内存的函数。我们会拿到 Bp 对象来进行调用,具体的实现则是在 Bn 中。
它实际上就是调用到了 HAL 部分的 allocate,这其实就有些像我们调用系统函数时的过程,也是通过 AIDL 完成的,只不过这里就是通过 HIDL 完成的。接下来我们需要知道这个 HAL 是如何完成工作的。
注意, 我们之前通过 IAllocator 调用了两个函数, 其中还有一个函数是 getService ,而这里的 hal 文件中我们并没有看到这个函数, 那么这个函数是哪里来的呢 其实在编译的时候, 还会生成一个文件 AllocatorAll.cpp , 这个文件就包含了我们之前调用的 getService
2.2.4 getService
[out/soong/.intermediates/hardware/interfaces/graphics/allocator/3.0/android.hardware.graphics.allocator@3.0_genc++/gen/android/hardware/graphics/allocator/3.0/AllocatorAll.cpp]
::android::sp<IAllocator> IAllocator::getService(const std::string &serviceName, const bool getStub) {
return ::android::hardware::details::getServiceInternal<BpHwAllocator>(serviceName, true, getStub);
}
到此,Framework 部分的内容就全部介绍完了,还是比较简单的,因为真正的工作都是发生在 HAL 层和硬件层。
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
需要的直接点击文末小卡片可以领取哦我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)
Android学习PDF+架构视频+面试文档+源码笔记
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)
- Android学习的系统对应视频
- Android进阶的系统对应学习资料
- Android BAT大厂面试题(有解析)
领取地址: