Android本地窗口
Android系统定义了一个Native窗口 ANativeWindow,结构定义如下:
struct ANativeWindow
{
#ifdef __cplusplus
ANativeWindow()
: flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
{
common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
common.version = sizeof(ANativeWindow);
memset(common.reserved, 0, sizeof(common.reserved));
}
/* Implement the methods that sp expects so that it
can be used to automatically refcount ANativeWindow's. */
void incStrong(const void* /*id*/) const {
common.incRef(const_cast(&common));
}
void decStrong(const void* /*id*/) const {
common.decRef(const_cast(&common));
}
#endif
struct android_native_base_t common;
/* flags describing some attributes of this surface or its updater */
const uint32_t flags;
/* min swap interval supported by this updated */
const int minSwapInterval;
/* max swap interval supported by this updated */
const int maxSwapInterval;
/* horizontal and vertical resolution in DPI */
const float xdpi;
const float ydpi;
/* Some storage reserved for the OEM's driver. */
intptr_t oem[4];
/*
* Set the swap interval for this surface.
*
* Returns 0 on success or -errno on error.
*/
int (*setSwapInterval)(struct ANativeWindow* window,
int interval);
/*
* Hook called by EGL to acquire a buffer. After this call, the buffer
* is not locked, so its content cannot be modified. This call may block if
* no buffers are available.
*
* The window holds a reference to the buffer between dequeueBuffer and
* either queueBuffer or cancelBuffer, so clients only need their own
* reference if they might use the buffer after queueing or canceling it.
* Holding a reference to a buffer after queueing or canceling it is only
* allowed if a specific buffer count has been set.
*
* Returns 0 on success or -errno on error.
*
* XXX: This function is deprecated. It will continue to work for some
* time for binary compatibility, but the new dequeueBuffer function that
* outputs a fence file descriptor should be used in its place.
*/
int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer** buffer);
/*
* hook called by EGL to lock a buffer. This MUST be called before modifying
* the content of a buffer. The buffer must have been acquired with
* dequeueBuffer first.
*
* Returns 0 on success or -errno on error.
*
* XXX: This function is deprecated. It will continue to work for some
* time for binary compatibility, but it is essentially a no-op, and calls
* to it should be removed.
*/
int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer);
/*
* Hook called by EGL when modifications to the render buffer are done.
* This unlocks and post the buffer.
*
* The window holds a reference to the buffer between dequeueBuffer and
* either queueBuffer or cancelBuffer, so clients only need their own
* reference if they might use the buffer after queueing or canceling it.
* Holding a reference to a buffer after queueing or canceling it is only
* allowed if a specific buffer count has been set.
*
* Buffers MUST be queued in the same order than they were dequeued.
*
* Returns 0 on success or -errno on error.
*
* XXX: This function is deprecated. It will continue to work for some
* time for binary compatibility, but the new queueBuffer function that
* takes a fence file descriptor should be used in its place (pass a value
* of -1 for the fence file descriptor if there is no valid one to pass).
*/
int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer);
/*
* hook used to retrieve information about the native window.
*
* Returns 0 on success or -errno on error.
*/
int (*query)(const struct ANativeWindow* window,
int what, int* value);
/*
* hook used to perform various operations on the surface.
* (*perform)() is a generic mechanism to add functionality to
* ANativeWindow while keeping backward binary compatibility.
*
* DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions
* defined below.
*
* (*perform)() returns -ENOENT if the 'what' parameter is not supported
* by the surface's implementation.
*
* See above for a list of valid operations, such as
* NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
*/
int (*perform)(struct ANativeWindow* window,
int operation, ... );
/*
* Hook used to cancel a buffer that has been dequeued.
* No synchronization is performed between dequeue() and cancel(), so
* either external synchronization is needed, or these functions must be
* called from the same thread.
*
* The window holds a reference to the buffer between dequeueBuffer and
* either queueBuffer or cancelBuffer, so clients only need their own
* reference if they might use the buffer after queueing or canceling it.
* Holding a reference to a buffer after queueing or canceling it is only
* allowed if a specific buffer count has been set.
*
* XXX: This function is deprecated. It will continue to work for some
* time for binary compatibility, but the new cancelBuffer function that
* takes a fence file descriptor should be used in its place (pass a value
* of -1 for the fence file descriptor if there is no valid one to pass).
*/
int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer);
/*
* Hook called by EGL to acquire a buffer. This call may block if no
* buffers are available.
*
* The window holds a reference to the buffer between dequeueBuffer and
* either queueBuffer or cancelBuffer, so clients only need their own
* reference if they might use the buffer after queueing or canceling it.
* Holding a reference to a buffer after queueing or canceling it is only
* allowed if a specific buffer count has been set.
*
* The libsync fence file descriptor returned in the int pointed to by the
* fenceFd argument will refer to the fence that must signal before the
* dequeued buffer may be written to. A value of -1 indicates that the
* caller may access the buffer immediately without waiting on a fence. If
* a valid file descriptor is returned (i.e. any value except -1) then the
* caller is responsible for closing the file descriptor.
*
* Returns 0 on success or -errno on error.
*/
int (*dequeueBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer** buffer, int* fenceFd);
/*
* Hook called by EGL when modifications to the render buffer are done.
* This unlocks and post the buffer.
*
* The window holds a reference to the buffer between dequeueBuffer and
* either queueBuffer or cancelBuffer, so clients only need their own
* reference if they might use the buffer after queueing or canceling it.
* Holding a reference to a buffer after queueing or canceling it is only
* allowed if a specific buffer count has been set.
*
* The fenceFd argument specifies a libsync fence file descriptor for a
* fence that must signal before the buffer can be accessed. If the buffer
* can be accessed immediately then a value of -1 should be used. The
* caller must not use the file descriptor after it is passed to
* queueBuffer, and the ANativeWindow implementation is responsible for
* closing it.
*
* Returns 0 on success or -errno on error.
*/
int (*queueBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer, int fenceFd);
/*
* Hook used to cancel a buffer that has been dequeued.
* No synchronization is performed between dequeue() and cancel(), so
* either external synchronization is needed, or these functions must be
* called from the same thread.
*
* The window holds a reference to the buffer between dequeueBuffer and
* either queueBuffer or cancelBuffer, so clients only need their own
* reference if they might use the buffer after queueing or canceling it.
* Holding a reference to a buffer after queueing or canceling it is only
* allowed if a specific buffer count has been set.
*
* The fenceFd argument specifies a libsync fence file decsriptor for a
* fence that must signal before the buffer can be accessed. If the buffer
* can be accessed immediately then a value of -1 should be used.
*
* Note that if the client has not waited on the fence that was returned
* from dequeueBuffer, that same fence should be passed to cancelBuffer to
* ensure that future uses of the buffer are preceded by a wait on that
* fence. The caller must not use the file descriptor after it is passed
* to cancelBuffer, and the ANativeWindow implementation is responsible for
* closing it.
*
* Returns 0 on success or -errno on error.
*/
int (*cancelBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer, int fenceFd);
};
根据ANativeWindow的结构定义可看出,ANativeWindow描述了本地窗口的基本信息,同时还定义了一系列操作窗口缓冲区的方法,窗口是需要内容显示的,显示的内容自然是存放在窗口缓冲区ANativeWindowBuffer中。
来看下ANativeWindowBuffer窗口缓存的结构定义:
typedef struct ANativeWindowBuffer
{
#ifdef __cplusplus
ANativeWindowBuffer() {
common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
common.version = sizeof(ANativeWindowBuffer);
memset(common.reserved, 0, sizeof(common.reserved));
}
// Implement the methods that sp expects so that it
// can be used to automatically refcount ANativeWindowBuffer's.
void incStrong(const void* /*id*/) const {
common.incRef(const_cast(&common));
}
void decStrong(const void* /*id*/) const {
common.decRef(const_cast(&common));
}
#endif
struct android_native_base_t common;
int width;
int height;
int stride;
int format;
int usage;
void* reserved[2];
buffer_handle_t handle;
void* reserved_proc[8];
} ANativeWindowBuffer_t;
ANativeWindowBuffer定义了窗口缓冲区的基本信息,包括宽,高, stride(每行像素个数),图像格式等,还定义了handle指针,存放Gralloc模块分配的真正的Buffer的地址。
ANativeWindow代表了一个窗口,提供了窗口管理的基本方法来操作窗口缓冲区的出队和入队。ANativeWindowBuffer则负责描述Window一个图形缓冲区。
Surface实现了ANativeWindow, 负责管理一个窗口,而GraphicBuffer则实现了ANativeWindowBuffer,负责管理一个图形缓冲区。Surface后续在进行分析,我们先看下GraphicBuffer的实现。
GraphicBuffer的实现
class GraphicBuffer
: public ANativeObjectBase< ANativeWindowBuffer, GraphicBuffer, RefBase >,
public Flattenable
GraphicBuffer继承自ANativeWindow
// creates w * h buffer
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inUsage);
// create a buffer from an existing handle
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle,
bool keepOwnership);
// create a buffer from an existing ANativeWindowBuffer
GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership);
GraphicBuffer有三个构造函数
1:创建一个width × height的GraphicBuffer
2:根据已经有的buffer的handle来创建一个GraphicBuffer
3:根据一个ANativeWindowBuffer来创建一个GraphicBuffer
我们只分析第一个看看GraphicBuffer是如何创建的?
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inUsage)
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
{
width=height=stride=format=usage=0;
handle = NULL;
mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage);
}
构造方法先将GraphicBuffer的width,height等属性设置为默认值0,然后调用initSize来创建Buffer.
status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inUsage)
{
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
uint32_t outStride = 0;
status_t err = allocator.alloc(inWidth, inHeight, inFormat, inUsage,
&handle, &outStride);
if (err == NO_ERROR) {
width = static_cast(inWidth);
height = static_cast(inHeight);
format = inFormat;
usage = static_cast(inUsage);
stride = static_cast(outStride);
}
return err;
}
initSize方法中首先获取了GraphicBufferAllocator对象, GraphicBufferAllocator实现是单例模式,所以每个进程中只有一个GraphicBufferAllocator负责Buffer的分配。
调用GraphicBufferAllocator的alloc方法为当前GraphicBuffer分配了一个指定宽高,以及Format的Buffer, 分配成功后将宽高,stride等属性保存到GraphicBuffer对象的属性中,把buffer的handle保存在GraphicBuffer的handle中,GraphicBuffer就可以管理一个真正的图形缓冲区Buffer,这样GraphicBuffer就创建好了。
GraphicBuffer图形缓冲区的分配和释放
GraphicBuffer在构造方法中调用了GraphicBufferAllocator来分配了一个真正的图形缓冲区,具体是怎么分配的呢?
GraphicBufferAllocator::GraphicBufferAllocator()
: mAllocDev(0)
{
hw_module_t const* module;
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
ALOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID);
if (err == 0) {
gralloc_open(module, &mAllocDev);
}
}
在分析Gralloc模块的时候知道,Gralloc设备会在两个地方被打开,一个是HWCompser,另一个就是GraphicBufferAllocator, 此处会加载Gralloc硬件模块抽象库,同时调用gralloc_open来打开GRALLOC_HARDWARE_GPU0 设备,将返回的alloc_device_t保存在mAllocDev中。
接着看负责缓冲区分配的alloc方法。
status_t GraphicBufferAllocator::alloc(uint32_t width, uint32_t height,
PixelFormat format, uint32_t usage, buffer_handle_t* handle,
uint32_t* stride)
{
ATRACE_CALL();
//如果没有传入Buffer的宽高尺寸,则默认创建一个1*1的Buffer
if (!width || !height)
width = height = 1;
...
int outStride = 0;
err = mAllocDev->alloc(mAllocDev, static_cast(width),
static_cast(height), format, static_cast(usage), handle,
&outStride);
*stride = static_cast(outStride);
//如果Buffer分配成功,则根据Buffer的属性创建一个alloc_rec_t 对象,保存在sAllocList中
if (err == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector& list(sAllocList);
uint32_t bpp = bytesPerPixel(format);
alloc_rec_t rec;
rec.width = width;
rec.height = height;
rec.stride = *stride;
rec.format = format;
rec.usage = usage;
rec.size = static_cast(height * (*stride) * bpp);
list.add(*handle, rec);
}
return err;
}
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
{
ATRACE_CALL();
status_t err;
err = mAllocDev->free(mAllocDev, handle);
if (err == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector& list(sAllocList);
//从sAllocList列表中移除
list.removeItem(handle);
}
return err;
}
alloc方法会调用打开的alloc_device_t设备的alloc方法,GraphicBufferAllocator中调用 mAllocDev->alloc方法会调用到gralloc模块,来分配图形缓冲区,如果分配成功之后则根据返回的属性创建一个alloc_rec_t对象,并将这个对象添加到sAllocList中。
sAllocList是GraphicBufferAllocator维护的一个列表,一个进程中所有分配的图形缓冲区都会维护在这个列表中,只有调用GraphicBufferAllocator的free方法释放缓冲区后,才会从列表中移除。
Gralloc模块图形缓冲区的分配和释放
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
gralloc_context_t *dev;
...
dev->device.common.close = gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free = gralloc_free;
} else {
...
}
}
alloc_device_t设备在打开的过程中会注册操作图形缓冲区的方法,gralloc_alloc和gralloc_free,两个方法分别 用于分配和释放图形缓冲区。
gralloc_alloc
static int gralloc_alloc(alloc_device_t* dev,
int width, int height, int format, int usage,
buffer_handle_t* pHandle, int* pStride)
{
if (!pHandle || !pStride)
return -EINVAL;
//根据formate来计算每个像素占几个字节
int bytesPerPixel = 0;
switch (format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_BGRA_8888:
bytesPerPixel = 4;
break;
case HAL_PIXEL_FORMAT_RGB_888:
bytesPerPixel = 3;
break;
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_RAW16:
bytesPerPixel = 2;
break;
default:
return -EINVAL;
}
const size_t tileWidth = 2;
const size_t tileHeight = 2;
//计算stride和size
size_t stride = align(width, tileWidth);
size_t size = align(height, tileHeight) * stride * bytesPerPixel + 4;
//因我们是操作图形缓冲区,而非FB显存,所以直接看gralloc_alloc_buffer方法
int err;
if (usage & GRALLOC_USAGE_HW_FB) {
...
} else {
err = gralloc_alloc_buffer(dev, size, usage, pHandle);
}
*pStride = stride;
return 0;
}
该方法首先根据传入的图像格式计算每个像素占用的字节数,根据每个像素的字节数计算每行像素的个数stride 以及 图形缓冲区所需要的内存大小size。
最后由这些结果作为参数调用gralloc_alloc_buffer来分配缓冲区内存。
static int gralloc_alloc_buffer(alloc_device_t* dev,
size_t size, int /*usage*/, buffer_handle_t* pHandle)
{
int err = 0;
int fd = -1;
//将缓冲区大小页对齐
size = roundUpToPageSize(size);
//创建一个匿名的共享内存,大小为size
fd = ashmem_create_region("gralloc-buffer", size);
if (fd < 0) {
ALOGE("couldn't create ashmem (%s)", strerror(-errno));
err = -errno;
}
//然后根据共享内存的文件描述符fd
if (err == 0) {
private_handle_t* hnd = new private_handle_t(fd, size, 0);
gralloc_module_t* module = reinterpret_cast(
dev->common.module);
err = mapBuffer(module, hnd);
if (err == 0) {
*pHandle = hnd;
}
}
ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
return err;
}
int mapBuffer(gralloc_module_t const* module,
private_handle_t* hnd)
{
void* vaddr;
return gralloc_map(module, hnd, &vaddr);
}
static int gralloc_map(gralloc_module_t const* /*module*/,
buffer_handle_t handle,
void** vaddr)
{
private_handle_t* hnd = (private_handle_t*)handle;
if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
size_t size = hnd->size;
//将共享内存映射到当前进程
void* mappedAddress = mmap(0, size,
PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);
//将首地址存放到private_handle_t的base变量中,返回给调用者
hnd->base = uintptr_t(mappedAddress) + hnd->offset;
}
*vaddr = (void*)hnd->base;
return 0;
}
gralloc_alloc_buffer分配缓冲区内存做了什么操作呢?
1:根据参数传入的缓冲区大小 创建了一个匿名共享内存,共享内存fd存放到private_handle_t的fd中
2:调用mapBuffer,将共享内存映射到当前进程,并吧内存首地址保存到private_handle_t的base中
3:将private_handle_t返回给调用者,这个就是图形缓冲区的handle, 用于唯一区别一个缓冲区。
至此,Gralloc模块分配缓冲区的逻辑就分析完了
gralloc_free
static int gralloc_free(alloc_device_t* dev,
buffer_handle_t handle)
{
if (private_handle_t::validate(handle) < 0)
return -EINVAL;
private_handle_t const* hnd = reinterpret_cast(handle);
if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
...
} else {
gralloc_module_t* module = reinterpret_cast(
dev->common.module);
terminateBuffer(module, const_cast(hnd));
}
close(hnd->fd);
delete hnd;
return 0;
}
根据传入的图形缓冲区的private_handle_t标识符,调用terminateBuffer来释放buffer, 因我们分析GraphicBuffer的分配与释放,FB设备的相关逻辑暂时不关注。
直接看terminateBuffer是如何释放缓冲区的?
int terminateBuffer(gralloc_module_t const* module,
private_handle_t* hnd)
{
if (hnd->base) {
//hnd的base中保存的是映射内存的首地址,如果这个属性不为0,说明地址已经映射过了,需要取消映射
gralloc_unmap(module, hnd);
}
return 0;
}
static int gralloc_unmap(gralloc_module_t const* /*module*/,
buffer_handle_t handle)
{
private_handle_t* hnd = (private_handle_t*)handle;
//如果不是FB设备,则直接调用munmap方法,取消映射
if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
void* base = (void*)hnd->base;
size_t size = hnd->size;
//ALOGD("unmapping from %p, size=%d", base, size);
if (munmap(base, size) < 0) {
ALOGE("Could not unmap %s", strerror(errno));
}
}
hnd->base = 0;
return 0;
}
terminateBuffer 释放图形缓冲区很简单,根据private_handle_t的base属性判断内存是否已经映射过了,如果已经映射,则调用munmap方法取消映射即可。
总结
GraphicBuffer实现了ANativeWindowBuffer,用来管理图像窗口的图形缓冲区,是ANativeWindow的显示内容和操作对象,GraphicBuffer的handle指针指向的才是真正的缓冲区内存。
GraphicBufferAllocator负责缓冲区内存的分配,会调用到gralloc模块的gralloc设备进行分配。每个图形缓冲区都是一个匿名共享内存空间,分配的时候创建一个匿名共享内存,然后映射到当前进程,释放的时候将匿名共享内存从当前进程取消映射。