SurfaceFlinger GraphicBuffer介绍
简介
在前面介绍BlastBufferQueue的时候我们经常提到GraphicBuffer,这篇文章就来介绍一下这个GraphicBuffer。因为GraphicBuffer最终实现是基于linux的drm。我们目前不会介绍太多关于drm的知识,所以这篇文章主要是介绍一下GraphicBuffer分配的通路流程,不会进一步去看linux的drm实现。
drm分配的buffer是dma buffer,dma buffer是一块可以在cpu和其他io外设共享的buffer,他关联一个物理buffer和一个文件fd,fd是媒介,我们就是通过fd将buffer在不同驱动之间流转。
GraphicBuffer构造
我们就从GraphicBuffer构造入手,来了解一下一下他们怎么调用到drm接口构造buffer的
1.1 GraphicBuffer::GraphicBuffer
构造函数传入来许多参数,然后调用initWithSize来初始化
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));
}
1.2 status_t GraphicBuffer::initWithHandle
GraphicBuffer继承自ANativeWindowBuffer,下面也列出了ANativeWindowBuffer结构体,标注了部分变量的含义。
initWithSize通过调用GraphicBufferAllocator::allocate分配一个buffer,然后将参数赋值到这些变量里
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
std::string requestorName)
{
// 分配buffer的逻辑在这里
// mBufferMapper是一个GraphicBufferMapper实例,我们先看下GraphicBufferMapper构造函数,见1.2.1
// allocate,详见1.3
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
uint32_t outStride = 0;
status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
inUsage, &handle, &outStride, mId,
std::move(requestorName));
if (err == NO_ERROR) {
// 将入参存到ANativeWindowBuffer结构体里
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;
}
typedef struct ANativeWindowBuffer
{
struct android_native_base_t common; // 里面有一个魔数,标记他是GraphicBuffer,有一个版本号,还有两个函数指针,分别是incRef,decRef
int width; // buffer代表的图像宽度
int height; // buffer代表的图像高度
int stride; // 步长,表示一个像素需要多少字节
int format; // 图像的编码格式,比如RGBA
int usage_deprecated;
uintptr_t layerCount;
void *reserved[1];
const native_handle_t *handle; // 句柄,指向底层的buffer
uint64_t usage;
void *reserved_proc[8 - (sizeof(uint64_t) / sizeof(void *))];
} ANativeWindowBuffer_t;
1.2.1 GraphicBufferAllocator::GraphicBufferAllocator()
我们一会会看到,实际调用allocate会调用mAllocator的allocate,这里不同版本就有不同的逻辑。
mAllocator也会调用GraphicBufferMapper的一些方法,GraphicBufferMapper的结构和GraphicBufferAllocator有一些类似的地方,我们看看他的构造函数就知道来,详见1.2.2。
GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
switch (mMapper.getMapperVersion()) {
case GraphicBufferMapper::GRALLOC_5:
mAllocator = std::make_unique<const Gralloc5Allocator>(
reinterpret_cast<const Gralloc5Mapper&>(mMapper.getGrallocMapper()));
break;
case GraphicBufferMapper::GRALLOC_4:
mAllocator = std::make_unique<const Gralloc4Allocator>(
reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper()));
break;
case GraphicBufferMapper::GRALLOC_3:
mAllocator = std::make_unique<const Gralloc3Allocator>(
reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
break;
case GraphicBufferMapper::GRALLOC_2:
mAllocator = std::make_unique<const Gralloc2Allocator>(
reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
break;
}
LOG_ALWAYS_FATAL_IF(!mAllocator->isLoaded(),
"Failed to load matching allocator for mapper version %d",
mMapper.getMapperVersion());
}
1.2.2 GraphicBufferMapper::GraphicBufferMapper
实际的逻辑都是通过调用mMapper实现的,mMapper是一个Mapper类型的变量,GraphicBufferMapper相当于是Mapper装饰类,通过这种方式去在这里判断使用不同版本的逻辑。
GraphicBufferMapper::GraphicBufferMapper() {
mMapper = std::make_unique<const Gralloc5Mapper>();
if (mMapper->isLoaded()) {
mMapperVersion = Version::GRALLOC_5;
return;
}
mMapper = std::make_unique<const Gralloc4Mapper>();
if (mMapper->isLoaded()) {
mMapperVersion = Version::GRALLOC_4;
return;
}
mMapper = std::make_unique<const Gralloc3Mapper>();
if (mMapper->isLoaded()) {
mMapperVersion = Version::GRALLOC_3;
return;
}
mMapper = std::make_unique<const Gralloc2Mapper>();
if (mMapper->isLoaded()) {
mMapperVersion = Version::GRALLOC_2;
return;
}
LOG_ALWAYS_FATAL("gralloc-mapper is missing");
}
1.3 GraphicBufferAllocator::allocate
什么都没做,就直接调用了allocateHelper
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,
std::string requestorName) {
// 其他什么也没做,直接调用了allocateHelper,详见1.4
return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
true);
}
1.4 GraphicBufferAllocator::allocateHelper
调用了mAllocator->allocate来分配buffer,前面1.2.1初始化来mAllocator变量,我们这里就看最新版本的,Gralloc5Allocator的流程。
后面计算了buffer大小,然后将buffer的信息和句柄存储到一个list中(实际是个map)。
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) {
// 。。。参数的基本校验
// 调用mAllocator->allocate,由1.2.1可知道,不同版本这里mAllocate不同,我们来分析Gralloc5Allocator的版本
// 详见1.5
status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
1, stride, handle, importBuffer);
// 。。。返回值错误情况处理
if (!importBuffer) {
return NO_ERROR;
}
size_t bufSize;
// 计算buffer大小
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;
}
// 将buffer的信息存储到list中
Mutex::Autolock _l(sLock);
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;
}
1.5 Gralloc5Allocator::allocate
这里的mAllocator是aidl,会binder调用到另一个进程实现最终的buffer分配。
下面调用的mMapper.importBuffer,这里的mMapper是加载了一个so,将里面的方法地址映射到mMapper中去。
我们先来看下mAllocator的初始化以及mMapper的方法映射,然后再来看allocate2的实现。
status_t Gralloc5Allocator::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 {
// 将参数封装到BufferDescriptorInfo内
auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage);
if (!descriptorInfo) {
return BAD_VALUE;
}
AllocationResult result;
// 这里mAllocator是一个aidl,这里会binder调用到另一个进程实现最终的buffer调用。
// 同时下面mMapper.importBuffer这个方法是执行一个so库里的逻辑,这个会根据不同版本做映射
// 继续往下看之前,我们来看一下这里的mAllocator赋值的地方,以及加载so,映射方法的逻辑,详见1.5.1。
// allocate2详见1.6
auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result);
// 。。。 错误检测
if (importBuffers) {
for (uint32_t i = 0; i < bufferCount; i++) {
auto handle = makeFromAidl(result.buffers[i]);
auto error = mMapper.importBuffer(handle, &outBufferHandles[i]);
native_handle_delete(handle);
if (error != NO_ERROR) {
for (uint32_t j = 0; j < i; j++) {
mMapper.freeBuffer(outBufferHandles[j]);
outBufferHandles[j] = nullptr;
}
return error;
}
}
} 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;
}
return NO_MEMORY;
}
}
}
*outStride = result.stride;
return OK;
}
1.5.1 Gralloc5Allocator::Gralloc5Allocator/Gralloc5Mapper::Gralloc5Mapper
在Gralloc5Allocator和Gralloc5Mapper构造函数都会调用getInstance,在getInstance里会构造mAllocator和mMapper。
其中在waitForAllocator中就会去获取IAllocator的binder handle,返回IAllocator。
然后回加载一个so,loadIMapper里会将so方法的一直填充到mMapper里。
这里会从IAllocator方法里获取so的一部分名字,也就是说不同版本的IAllocator就会对应不同版本的so。
Gralloc5Allocator::Gralloc5Allocator(const Gralloc5Mapper &mapper) : mMapper(mapper) {
mAllocator = getInstance().allocator;
}
Gralloc5Mapper::Gralloc5Mapper() {
mMapper = getInstance().mapper;
}
static const Gralloc5 &getInstance() {
static Gralloc5 instance = []() {
// 这里的在等IAllocator::descriptor + std::string("/default")这个名字的service
// 这里是一个binder, 也就是说真正实现的分配buffer的逻辑是在另一个进程里,我们通过binder去请求那个进程来分配
// 并且会获取一个IAllocator对象,就是binder的client端。
auto allocator = waitForAllocator();
if (!allocator) {
return Gralloc5{};
}
// 这里会通过IAllocator binder调用getIMapperLibrarySuffix获取一个前缀,然后和固定的文本拼接出一个so库的名字,然后加载这个so
void *so = loadIMapperLibrary();
if (!so) {
return Gralloc5{};
}
// 加载这个so中的方法,这个方法是用来填充AIMapper的,AIMapper里面是一个版本号和一些函数指针。
auto loadIMapper = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
AIMapper *mapper = nullptr;
// 通过so中的方法来填充AIMapper,主要是填充里面的函数指针,后续就可以通过这些函数来访问对端做buffer的操作了,详见1.5.3
AIMapper_Error error = loadIMapper(&mapper);
if (error != AIMAPPER_ERROR_NONE) {
ALOGE("AIMapper_loadIMapper failed %d", error);
return Gralloc5{};
}
return Gralloc5{std::move(allocator), mapper};
}();
return instance;
}
1.5.2 waitForAllocator
获取IAllocator binder代理
static std::shared_ptr<IAllocator> waitForAllocator() {
if (__builtin_available(android 31, *)) {
if (!AServiceManager_isDeclared(kIAllocatorServiceName.c_str())) {
return nullptr;
}
// 就是获取一个IAllocator binder代理
auto allocator = IAllocator::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(kIAllocatorServiceName.c_str())));
// 。。。
return allocator;
} else {
// TODO: LOG_ALWAYS_FATAL("libui is not backwards compatible");
return nullptr;
}
}
1.5.3 load
映射逻辑的代码路径在/hardware/interfaces/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperProvider.h
这里前面还有一层调用就不看来,最终是调用这个load方法,通过bindV5绑定方法地址
AIMapper_Error load(AIMapper* _Nullable* _Nonnull outImplementation) {
std::call_once(mLoadOnceFlag, [this] {
LOG_ALWAYS_FATAL_IF(provider::sIMapperInstance != nullptr,
"AIMapper implementation already loaded!");
provider::sIMapperInstance = this;
mImpl.emplace();
mMapper.version = IMPL::version;
if (IMPL::version >= AIMAPPER_VERSION_5) {
// 绑定函数地址,详见1.5.4
bindV5();
}
});
*outImplementation = &mMapper;
return AIMAPPER_ERROR_NONE;
}
1.5.4 bindV5
void bindV5() {
mMapper.v5 = {
.importBuffer = [](const native_handle_t* _Nonnull handle,
buffer_handle_t _Nullable* _Nonnull outBufferHandle)
-> AIMapper_Error { return impl().importBuffer(handle, outBufferHandle); },
.freeBuffer = [](buffer_handle_t _Nonnull buffer) -> AIMapper_Error {
return impl().freeBuffer(buffer);
},
// 。。。下面都是类似的方法绑定,这里的impl是template模板构造的时候传入的,这里是CrosGrallocMapperV5
// 所以1.5的mMapper.importBuffer最终调用的是CrosGrallocMapperV5::importBuffer
};
}
1.6 Allocator::allocate2
这里的文件路径在/external/minigbm/cros_gralloc/aidl/Allocator.cpp
ndk::ScopedAStatus Allocator::allocate2(const BufferDescriptorInfo& descriptor, int32_t count,
allocator::AllocationResult* outResult) {
// 。。。初始化检测
// client侧将buffer相关的参数都封装到BufferDescriptorInfo里
// 这里将BufferDescriptorInfo转换成一个BufferDescriptorInfoV4对象,他们内部的字段都差不多
BufferDescriptorInfoV4 descriptionV4 = convertAidlToIMapperV4Descriptor(descriptor);
std::vector<native_handle_t*> handles;
handles.resize(count, nullptr);
// count是需要分配的buffer个数,就是GraphicBuffer的layerCount决定的。
for (int32_t i = 0; i < count; i++) {
ndk::ScopedAStatus status = allocate(descriptionV4, &outResult->stride, &handles[i]);
if (!status.isOk()) {
for (int32_t j = 0; j < i; j++) {
// 如果分配失败了,就把之前分配的全部释放掉。
releaseBufferAndHandle(handles[j]);
}
return status;
}
}
outResult->buffers.resize(count);
for (int32_t i = 0; i < count; i++) {
auto handle = handles[i];
outResult->buffers[i] = ::android::dupToAidl(handle);
releaseBufferAndHandle(handle);
}
return ndk::ScopedAStatus::ok();
}
1.7 Allocator::allocate
ndk::ScopedAStatus Allocator::allocate(const BufferDescriptorInfoV4& descriptor, int32_t* outStride,
native_handle_t** outHandle) {
// 。。。初始化检测
// 这里又把BufferDescriptorInfoV4转化成cros_gralloc_buffer_descriptor格式。
struct cros_gralloc_buffer_descriptor crosDescriptor;
if (convertToCrosDescriptor(descriptor, &crosDescriptor)) {
return ToBinderStatus(AllocationError::UNSUPPORTED);
}
crosDescriptor.reserved_region_size += sizeof(CrosGralloc4Metadata);
// 。。。格式检测
native_handle_t* handle;
// 调用mDriver->allocate分配Buffer,mDriver是cros_gralloc_driver的实例
// 详见1.8
int ret = mDriver->allocate(&crosDescriptor, &handle);
// 。。。返回值处理
cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(handle);
auto status = initializeMetadata(crosHandle, crosDescriptor);
// 。。。返回值处理
*outStride = static_cast<int32_t>(crosHandle->pixel_stride);
*outHandle = handle;
return ndk::ScopedAStatus::ok();
}
1.8 cros_gralloc_driver::allocate
这段代码可能比较难懂,不是代码逻辑很复杂,主要是因为我们之前说过Android现在的GUI架构最终是基于linux drm的渲染架构的,这里用到来drm的一些接口,需要有一些预备知识。
我们不会对这段介绍的太深入,主要就是看一下大体流程,让大家不会觉得GraphicBuffer那么抽象。
drm是会提供几个文件节点,然后通过iocal的方式来个drm进行交互,操作显卡硬件。
libdrm是对这些操作进行了封装,提供的api给上层使用。
我们接下来看到的大多数的drm开头的方法都是libdrm里面的方法。
int32_t cros_gralloc_driver::allocate(const struct cros_gralloc_buffer_descriptor *descriptor,
native_handle_t **out_handle)
{
// 。。。
// 这里会构建一个bo结构,bo结构里面有一个driver指针,记录这里传入的drv
// 有一个bo_handle数组,这里记录的是drm构建的buffer的handle。
// bo_metadata里面记录来一些基础信息,比如width,height,format等。
// 这里的drv是一个driver结构体,里面有一个fd,这个fd就是drm提供的驱动节点的fd(/dev/dri/renderX或者/dev/dri/cardX)
// driver结构体还有一个backend结构体指针,里面是一些函数指针,不同的硬件会有不同的实现。
// buffer的分配也在这里面实现的,详见1.9
bo = drv_bo_create(drv_.get(), descriptor->width, descriptor->height, resolved_format,
resolved_use_flags);
// 。。。
// cros_gralloc_handle是最后返回回去的对象,会将前面bo里面的信息都填充到hnd里面。
hnd =
reinterpret_cast<struct cros_gralloc_handle *>(native_handle_create(num_fds, num_ints));
// 。。。
hnd->num_planes = num_planes;
for (size_t plane = 0; plane < num_planes; plane++) {
// 调用drm相关接口将buffer handle转换成fd存储到hnd,返回给请求的应用需要跨进程传输,所以需要转换成fd。
ret = drv_bo_get_plane_fd(bo, plane);
if (ret < 0)
goto destroy_hnd;
hnd->fds[plane] = ret;
hnd->strides[plane] = drv_bo_get_plane_stride(bo, plane);
hnd->offsets[plane] = drv_bo_get_plane_offset(bo, plane);
hnd->sizes[plane] = drv_bo_get_plane_size(bo, plane);
}
// 将基础信息都填充到hnd里
// 将bo和hnd封装成cros_gralloc_buffer
buffer = cros_gralloc_buffer::create(bo, hnd);
// 。。。
*out_handle = hnd;
return 0;
// 。。。
}
1.9 drv_bo_create
struct bo *drv_bo_create(struct driver *drv, uint32_t width, uint32_t height, uint32_t format,
uint64_t use_flags)
{
int ret;
struct bo *bo;
bool is_test_alloc;
is_test_alloc = use_flags & BO_USE_TEST_ALLOC;
use_flags &= ~BO_USE_TEST_ALLOC;
// 这里会new一个bo结构体,然后用参数填充结构体。
bo = drv_bo_new(drv, width, height, format, use_flags, is_test_alloc);
if (!bo)
return NULL;
ret = -EINVAL;
if (drv->backend->bo_compute_metadata) {
ret = drv->backend->bo_compute_metadata(bo, width, height, format, use_flags, NULL,
0);
if (!is_test_alloc && ret == 0)
ret = drv->backend->bo_create_from_metadata(bo);
} else if (!is_test_alloc) {
// 前面说过backend是基于不同硬件不同的一组函数指针。
// 我们随便找一个简单一些的实现来看一眼,详见1.10
ret = drv->backend->bo_create(bo, width, height, format, use_flags);
}
if (ret) {
errno = -ret;
free(bo);
return NULL;
}
drv_bo_acquire(bo);
return bo;
}
1.10 bo_create
在这里我们可以看到这个buffer是由drm驱动提供的接口分配出来的,分配buffer后会返回一个handle存储在出参create_dumb,然后会把这个handle放到bo结构中
由drm 接口分配的buffer就是dma buffer,这里的fd返回给请求侧,后续就可以通过fd在不同驱动中使用这个buffer了。
#define INIT_DUMB_DRIVER_WITH_NAME(driver, _name) \
const struct backend backend_##driver = { \
.name = _name, \
.init = dumb_driver_init, \
// bo_create在这里
.bo_create = drv_dumb_bo_create, \
.bo_create_with_modifiers = dumb_bo_create_with_modifiers, \
.bo_destroy = drv_dumb_bo_destroy, \
.bo_import = drv_prime_bo_import, \
.bo_map = drv_dumb_bo_map, \
.bo_unmap = drv_bo_munmap, \
.resolve_format_and_use_flags = drv_resolve_format_and_use_flags_helper, \
};
int drv_dumb_bo_create(struct bo *bo, uint32_t width, uint32_t height, uint32_t format,
uint64_t use_flags)
{
return drv_dumb_bo_create_ex(bo, width, height, format, use_flags, BO_QUIRK_NONE);
}
int drv_dumb_bo_create_ex(struct bo *bo, uint32_t width, uint32_t height, uint32_t format,
uint64_t use_flags, uint64_t quirks)
{
// 。。。参数填充构造的细节我们就不看了
// 可以看到最终调用的drm构建buffer,返回的create_dumb.handle就是buffer的句柄来,我们会存到bo结构里
ret = drmIoctl(bo->drv->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
// 。。。
for (plane = 0; plane < bo->meta.num_planes; plane++)
//将buffer句柄存到bo结构
bo->handles[plane].u32 = create_dumb.handle;
bo->meta.total_size = create_dumb.size;
return 0;
}
总结
我们介绍了GraphicBuffer构造流程,它是如何通过binder到分配服务,然后调用drm的接口进行buffern分配。
关于drm,如果后面有机会我们再去深入学习(估计是遥远的以后了,因为我们会先学习framework的一些核心服务,还是以android的内容为主)
dma-buffer我们目前也没去深入了解,目前就是大致知道他的作用是通过物理内存buffer关联fd,然后以fd为媒介在不同驱动中流转,使得不同驱动可以共享buffer。(同样是遥远的以后可能会深入的学习他)