文章目录
GraphicBuffer和Gralloc分析
BufferQueue中的Buffer对象,我们用的都是GraphicBuffer,那么GraphicBuffer是怎么来的呢?接下里我们具体来看这里的流程。
Surface是Andorid窗口的描述,是ANativeWindow的实现;同样GraphicBuffer是Android中图形Buffer的描述,是ANativeWindowBuffer的实现。而一个窗口,可以有几个Buffer。
GraphicBuffer定义
* frameworks/native/include/ui/GraphicBuffer.h
class GraphicBuffer
: public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase>,
public Flattenable<GraphicBuffer>
{
friend class Flattenable<GraphicBuffer>;
public:
其中ANativeObjectBase是一个模板类,定义如下:
* frameworks/native/include/ui/ANativeObjectBase.h
template <typename NATIVE_TYPE, typename TYPE, typename REF,
typename NATIVE_BASE = android_native_base_t>
class ANativeObjectBase : public NATIVE_TYPE, public REF
{
public:
// Disambiguate between the incStrong in REF and NATIVE_TYPE
void incStrong(const void* id) const {
REF::incStrong(id);
}
void decStrong(const void* id) const {
REF::decStrong(id);
}
这样ANativeObjectBase继承ANativeWindowBuffer和RefBase,GraphicBuffer继承ANativeObjectBase和Flattenable。
这样做的目的:
- RefBase使GraphicBuffer支持引用计数控制
- Flattenable使GraphicBuffer支持序列化。
其中的关键类 ANativeWindowBuffer,它是一个结构体,是对Native Buffer的一个描述,其定义如下:
* frameworks/native/libs/nativebase/include/nativebase/nativebase.h
typedef struct ANativeWindowBuffer
{
#ifdef __cplusplus
// 构造函数,decStrong和incStrong的实现;得初始化common
#endif
struct android_native_base_t common;
int width;
int height;
int stride;
int format;
int usage_deprecated;
uintptr_t layerCount;
void* reserved[1];
const native_handle_t* handle;
uint64_t usage;
void* reserved_proc[8 - (sizeof(uint64_t) / sizeof(void*))];
} ANativeWindowBuffer_t;
typedef struct ANativeWindowBuffer ANativeWindowBuffer;
// Old typedef for backwards compatibility.
typedef ANativeWindowBuffer_t android_native_buffer_t;
ANativeWindowBuffer中,很多属性前面我们介绍Surface时,已经介绍过了。这里重点看看这个native_handle_t。
* system/core/libcutils/include/cutils/native_handle.h
typedef struct native_handle
{
int version; /* sizeof(native_handle_t) */
int numFds; /* number of file-descriptors at &data[0] */
int numInts; /* number of ints at &data[numFds] */
... ...
int data[0]; /* numFds + numInts ints */
... ...
} native_handle_t;
typedef const native_handle_t* buffer_handle_t;
native_handle_t也就是具体Buffer的句柄,根据native_handle_t就能找到护体的Buffer。这里是用文件描述符进行描述的。
GraphicBuffer,很多属性都是继承于父类的,GraphicBuffer自己的属性比较少
* frameworks/native/include/ui/GraphicBuffer.h
uint8_t mOwner;
... ...
GraphicBufferMapper& mBufferMapper;
ssize_t mInitCheck;
// numbers of fds/ints in native_handle_t to flatten
uint32_t mTransportNumFds;
uint32_t mTransportNumInts;
uint64_t mId;
// Stores the generation number of this buffer. If this number does not
// match the BufferQueue's internal generation number (set through
// IGBP::setGenerationNumber), attempts to attach the buffer will fail.
uint32_t mGenerationNumber;
};
- mOwner
表示该GraphicBuffer持有的只是handle,还是持有具体的数据
enum {
ownNone = 0,
ownHandle = 1,
ownData = 2,
};
mOwner不一样,释放时,流程不一样:
void GraphicBuffer::free_handle()
{
if (mOwner == ownHandle) {
mBufferMapper.freeBuffer(handle);
} else if (mOwner == ownData) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
}
handle = NULL;
}
-
GraphicBufferMapper
GraphicBuffer实现Flattenable,可以将GraphicBuffer进行打包,在Binder中传递,但是传递只是Buffer的描述属性,并不真正去拷贝Buffer的内容。怎么实现的共享的,关键还是这里的handle。GraphicBufferMapper会根据handle去在不同的进程中进map,map到同一块物理内存。这里先埋个伏笔,后续我们会讲到。 -
mId
GraphicBuffer的ID,这个ID在不同进程中都是一样的 -
mGenerationNumber
可以理解问题这个buffer被用多少次了。如果这个值和BufferQueue中的mGenerationNumber不一直,那么是不能attach的。
余下,GraphicBuffer的相关函数我们接下来具体来看~
分配一块Buffer
Producer dequeueBuffer的时候,并不是 每一次都会去分配一块Buffer。还记得什么时候回去分配Buffer吗?没错,设置了标识BUFFER_NEEDS_REALLOCATION时。
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
此时分配的Buffer,参数比较齐全,对应的构造函数为:
* frameworks/native/libs/ui/GraphicBuffer.cpp
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage, std::string requestorName)
: GraphicBuffer()
{
mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount,
usage, std::move(requestorName));
}
在默认构造函数中,主要是做变量是初始化:
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
{
width =
height =
stride =
format =
usage_deprecated = 0;
usage = 0;
layerCount = 0;
handle = NULL;
}
mOwner默认是ownData。GraphicBufferMapper是一个单例类,mBufferMapper在每个进程中只有一个实际对象。inLayerCount为1,在BufferQueueProducer中是一个常量。
static constexpr uint32_t BQ_LAYER_COUNT = 1;
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
std::string requestorName)
{
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) {
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
Buffer管理中,另外一个单例类,GraphicBufferAllocator把Buffer分配出来你,GraphicBufferMapper可以将其map到自己的进程。
需要主要的是,BufferQueueProducer是跑在SurfaceFlinger进程中的,也就是说,绝大部分的应用,使用的Buffer,都是SurfaceFlinger进程分配出来的,所以,如果SurfaceFlinger出现内存泄露,FD泄露等问题,很有可能都是应用没有释放,SurfaceFlinger不会主动释放,它只响应应用的请求。SurfaceFlinger是背锅侠!
GraphicBufferAllocator定义如下:
* frameworks/native/include/ui/GraphicBufferAllocator.h
class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
{
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
std::string requestorName);
status_t free(buffer_handle_t handle);
void dump(String8& res) const;
static void dumpToSystemLog();
private:
struct alloc_rec_t {
uint32_t width;
uint32_t height;
uint32_t stride;
PixelFormat format;
uint32_t layerCount;
uint64_t usage;
size_t size;
std::string requestorName;
};
static Mutex sLock;
static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
friend class Singleton<GraphicBufferAllocator>;
GraphicBufferAllocator();
~GraphicBufferAllocator();
GraphicBufferMapper& mMapper;
const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
};
- 两个主要的方法,一个allocate用来分配Buffer,一个free用来释放Buffe。
- sAllocList,申请的Buffer,都保存下来,放到sAllocList中,并不是保存具体的Buffer,而是Buffer的描述alloc_rec_t。
- mAllocator,Gralloc登场,gralloc采用版本化管理,用的是Gralloc2。
GraphicBufferAllocator的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)
{
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
// allowed from an API stand-point allocate a 1x1 buffer instead.
if (!width || !height)
width = height = 1;
// Ensure that layerCount is valid.
if (layerCount < 1)
layerCount = 1;
Gralloc2::IMapper::BufferDescriptorInfo info = {};
info.width = width;
info.height = height;
info.layerCount = layerCount;
info.format = static_cast<Gralloc2::PixelFormat>(format);
info.usage = usage;
Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
if (error == Gralloc2::Error::NONE) {
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 {
ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
"usage %" PRIx64 ": %d",
width, height, layerCount, format, usage,
error);
return NO_MEMORY;
}
}
在看allocate函数之前,我们先来看一下GraphicBuffer相关的类:
GraphicBuffer的左膀右臂,GraphicBufferAllocator和GraphicBufferMapper!从Android 8.0开始,Android 操作系统框架在架构方面的一项重大改变,提出了treble 项目。Vendor的实现和Androd的实现分开,Android和HAL,采用HwBinder进行通信,减少Android对HAL的直接依赖。这里的Allocator和Mapper,就是对HAL结合的包装;IAllocator,IMapper的HAL的接口。V2_1::IMapper是一个对Gralloc HAL的2.1版本。
回到allocate函数~
BufferDescriptorInfo,对Buffer的描述,在HAL层也通用。根据需要,生成BufferDescriptorInfo,再通过Gralloc2的Allocator进行allocate。allocate出来的Buffer 句柄,保存在sAllocList中。
Gralloc2 Allocator的allocate函数提供了很多形态,可以满足我们不同的要求:
* frameworks/native/libs/ui/include/ui/Gralloc2.h
/*
* The returned buffers are already imported and must not be imported
* again. outBufferHandles must point to a space that can contain at
* least "count" buffer_handle_t.
*/
Error allocate(BufferDescriptor descriptor, uint32_t count,
uint32_t* outStride, buffer_handle_t* outBufferHandles) const;
Error allocate(BufferDescriptor descriptor,
uint32_t* outStride, buffer_handle_t* outBufferHandle) const
{
return allocate(descriptor, 1, outStride, outBufferHandle);
}
Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
uint32_t* outStride, buffer_handle_t* outBufferHandles) const
{
BufferDescriptor descriptor;
Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
if (error == Error::NONE) {
error = allocate(descriptor, count, outStride, outBufferHandles);
}
return error;
}
Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
uint32_t* outStride, buffer_handle_t* outBufferHandle) const
{
return allocate(descriptorInfo, 1, outStride, outBufferHandle);
}
我们传的参数是BufferDescriptorInfo,首先要根据BufferDescriptorInfo,生成一个BufferDescriptor,这个是mapper的HAL层实现的,因为这个BufferDescriptor最后也是要给到HAL层,HAL层根据BufferDescriptor去生成相应描述的Buffer。
最后,allocate的通用实现如下:
* frameworks/native/libs/ui/Gralloc2.cpp
Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
uint32_t* outStride, buffer_handle_t* outBufferHandles) const
{
Error error;
auto ret = mAllocator->allocate(descriptor, count,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
error = tmpError;
if (tmpError != Error::NONE) {
return;
}
// import buffers
for (uint32_t i = 0; i < count; i++) {
error = mMapper.importBuffer(tmpBuffers[i],
&outBufferHandles[i]);
if (error != Error::NONE) {
for (uint32_t j = 0; j < i; j++) {
mMapper.freeBuffer(outBufferHandles[j]);
outBufferHandles[j] = nullptr;
}
return;
}
}
*outStride = tmpStride;
});
// make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
hardware::IPCThreadState::self()->flushCommands();
return (ret.isOk()) ? error : kTrans