Android显示系统(01)- 架构分析
Android显示系统(02)- OpenGL ES - 概述
Android显示系统(03)- OpenGL ES - GLSurfaceView的使用
Android显示系统(04)- OpenGL ES - Shader绘制三角形
Android显示系统(05)- OpenGL ES - Shader绘制三角形(使用glsl文件)
Android显示系统(06)- OpenGL ES - VBO和EBO和VAO
Android显示系统(07)- OpenGL ES - 纹理Texture
Android显示系统(08)- OpenGL ES - 图片拉伸
Android显示系统(09)- SurfaceFlinger的使用
Android显示系统(10)- SurfaceFlinger内部结构
Android显示系统(11)- 向SurfaceFlinger申请Surface
Android显示系统(12)- 向SurfaceFlinger申请Buffer
Android显示系统(13)- 向SurfaceFlinger提交Buffer
一、前言:
前面我们申请了Surface,也就是一块画布,要往Surface上面绘图,是不是得有一个存放数据的地方呢?也就是Buffer。并且我们之前介绍过一个APP可以有多个Surface,但是一般只有一个
,同样,我们一个Surface也可以有多个Buffer,Android 10当中最多可以有64个。我们可以思考下为什么要有这么多,是不是刚启动就给分配64个Buffer?带着问题,我们开始今天申请Buffer的程序学习。
二、流程图:
其实这些Buffer资源都是SF侧管理的,并且我在 《Android显示系统(01)- 架构分析》这篇文章里面也分析了,最终SF会通过Gralloc模块向Ashmem模块申请Buffer;那么什么时候该申请呢?看下图:
- 在用户调用Surface的
dequeueBuffer
的时候(其实是先调用lock,lock里面调用dequeue); - 如果SF收到请求之后,发现自己本地的Buffer全都用完了,返回BUFFER_NEEDS_REALLOCATION,表示需要重新分配;
- 那么Surface再去调用
requestBuffer
向SF申请分配; - SF收到request之后,去向Gralloc模块申请,最终分配的是Ashmem模块,最终将Buffer的fd返回给Surface,Surface收到之后,其实就是Binder转换的一个副本;
- Surface收到之后,通过mmap(里面会调用Gralloc的HAL层进行mmap)将
fd'
的内存得到buffer地址。
三、EGL和Surface的关系:
3.1、EGLSurface 和 Surface 的绑定
在 EGL 和 Surface 的绑定过程中,EGL 并不直接实现具体的 dequeueBuffer
和 queueBuffer
。相反,这些操作是通过 android::Surface
和底层 GraphicBuffer
(通过 Gralloc 分配的内存)完成的。
EGLSurface
的创建(伪代码):
EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay,eglConfig,nativeWindow, // 最终传入的是从 SurfaceFlinger 获取的 android::Surface 的句柄nullptr
);
此处的 nativeWindow
是一个 ANativeWindow
,它底层其实指向的是 Android 的 android::Surface
对象。
-
在
eglCreateWindowSurface
中:-
EGL 会将传入的 NativeWindow(即
android::Surface
)与 EGLSurface 状态绑定。 -
之后,EGL 调用
Surface
的相关接口来完成底层缓冲区的管理。
-
对于 Android 平台,底层的 Surface
是和 Gralloc 绑定的,通过 Gralloc 分配图像缓冲区。
3.2、调用关系:
在整个流程中,要理解 dequeueBuffer
和 queueBuffer
如何实际被调用,需要深入了解 Android 的图形栈:
-
Surface 和 BufferQueue 的关系:
-
每个
Surface
背后都有一个BufferQueue
,它是一个生产者-消费者队列:- 生产者(Producer):EGL/OpenGL 等图形 API,可以通过
Surface
提供的 API 将图像缓冲区填充内容。 - 消费者(Consumer):通常是 SurfaceFlinger,它从
BufferQueue
中提取图像缓冲区进行最终的合成与显示。
- 生产者(Producer):EGL/OpenGL 等图形 API,可以通过
-
-
dequeueBuffer
的调用链: 当你在 OpenGL 渲染时调用eglSwapBuffers
时:- EGL 会调用
EGLSurface
绑定的 NativeWindow(android::Surface
)的dequeueBuffer
方法,取出一个可供写入的缓冲区(GraphicBuffer
)。 dequeueBuffer
的实现会请求 BufferQueue 分配或复用内存缓冲区,底层由 Gralloc HAL 提供支持。
- EGL 会调用
-
queueBuffer
的调用链:- 渲染完成后,调用
queueBuffer
将填充好的缓冲区返回给 BufferQueue。 - BufferQueue 将其传递给 SurfaceFlinger 进行下一步处理。
- 渲染完成后,调用
3.3、小结:
EGL 内部并不直接处理缓冲区管理,而是依赖绑定的 NativeWindow(即 Surface
对象)以及 BufferQueue 完成:
- EGL 和 NativeWindow 绑定:通过
eglCreateWindowSurface
,将 OpenGL 渲染操作与 Android Surface 互通。 - 调用接口:
eglSwapBuffers
内部调用链最终会触发native_window_dequeue_buffer_and_wait
,映射到Surface
的dequeueBuffer
。- 完成渲染后,通过
queueBuffer
将缓冲区提交。
BootAnimation 的作用就是通过这种机制将 OpenGL 绘制的帧推送到 Android 的显示系统,从而呈现动画。
四、代码走读:
根据上面的解释,我们就来走读Surface的代码,先看看lock接口,一般应用层都是surface都是先lock
然后unlockAndPost
:
文件路径:native\libs\gui\Surface.cpp
:
/*** 锁定一个 Surface 并获取绘制缓冲区;* @param outBuffer:保存到的锁定缓冲区信息,包括宽高、stride、指针等* @param inOutDirtyBounds: 输入输出的脏区域 bounds,用于优化更新区域*/
status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{// 检查当前对象是否已经被锁定,如果已锁定,直接返回错误if (mLockedBuffer != nullptr) {ALOGE("Surface::lock failed, already locked");return INVALID_OPERATION;}// 设置缓冲区的使用模式标志,表示读写操作是由软件(CPU)完成if (!mConnectedToCpu) {int err = Surface::connect(NATIVE_WINDOW_API_CPU);if (err) {return err;}// we're intending to do software rendering from this pointsetUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);}// 用于保存 dequeueBuffer 获取到的缓冲区ANativeWindowBuffer* out;// 用于获取缓冲区的同步 fenceint fenceFd = -1;// 调用 GraphicBufferProducer 的 dequeueBuffer 获取生产者(Producer)队列中的后缓冲区status_t err = dequeueBuffer(&out, &fenceFd);ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));if (err == NO_ERROR) {// 此时获取到一个合法的 backBuffer(后缓冲区)sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); // 包装为 GraphicBuffer 对象const Rect bounds(backBuffer->width, backBuffer->height); // 获取缓冲区的尺寸边界// 处理脏区域Region newDirtyRegion;if (inOutDirtyBounds) {// 如果提供了脏区域,更新 dirtyRegion,并限制到缓冲区的边界newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));// 保证脏区域不超越缓冲区范围newDirtyRegion.andSelf(bounds);} else {// 如果没有提供脏区域,默认整个缓冲区都是脏的newDirtyRegion.set(bounds);}// figure out if we can copy the frontbuffer back// 检查是否可以从前缓冲区复制内容到后缓冲区(优化绘制效率)const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);const bool canCopyBack = (frontBuffer != nullptr &&backBuffer->width == frontBuffer->width &&backBuffer->height == frontBuffer->height &&backBuffer->format == frontBuffer->format);if (canCopyBack) {// copy the area that is invalid and not repainted this round// 如果可以回读部分内容,则计算需要拷贝的内容区域const Region copyback(mDirtyRegion.subtract(newDirtyRegion));if (!copyback.isEmpty()) {// 执行内容拷贝操作,将前缓冲区的内容复制到后缓冲区copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);}} else {// if we can't copy-back anything, modify the user's dirty// region to make sure they redraw the whole buffer// 如果无法回读内容,将整个缓冲区标记为脏区域,要求完全重新绘制newDirtyRegion.set(bounds);// 清空脏区域的所有状态,并更新缓冲区的脏区域记录mDirtyRegion.clear();Mutex::Autolock lock(mMutex);for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {mSlots[i].dirtyRegion.clear();}}{ // scope for the lock// 更新缓冲区的脏区域信息,加一个锁避免冲突Mutex::Autolock lock(mMutex);// 获取后缓冲区的槽位int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));if (backBufferSlot >= 0) {Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);// 从全局脏区域中减去这部分mDirtyRegion.subtract(dirtyRegion);// 更新槽位的脏区域dirtyRegion = newDirtyRegion;}}// 将新的脏区域添加到全局脏区域中mDirtyRegion.orSelf(newDirtyRegion);if (inOutDirtyBounds) {*inOutDirtyBounds = newDirtyRegion.getBounds();}// 锁定缓冲区并获取指针(vaddr)// 调用 GraphicBuffer 的 lockAsync 来实际锁定缓冲区并得到内存地址void* vaddr;status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,newDirtyRegion.bounds(), &vaddr, fenceFd);ALOGW_IF(res, "failed locking buffer (handle = %p)",backBuffer->handle);if (res != 0) {err = INVALID_OPERATION;} else {// 锁定成功,保存缓冲区的信息到 outBuffermLockedBuffer = backBuffer;outBuffer->width = backBuffer->width;outBuffer->height = backBuffer->height;outBuffer->stride = backBuffer->stride;outBuffer->format = backBuffer->format;outBuffer->bits = vaddr; // 保存缓冲区 CPU 可写的内存地址}}return err;
}
关键点总结:
- 脏区域:
- 在 GPU 渲染优化的情况下,只需要更新"脏区域"(发生变化的区域)的内容,无需重绘整个缓冲区。
- 这个函数会根据输入指示的脏区域和实际需要绘制的区域来相应调整,并尝试从前缓冲区恢复未修改的部分。
- 缓冲区管理:
dequeueBuffer
是从缓冲区生产者(GraphicBufferProducer
)队列中获取一个后缓冲区。queueBuffer
是提交处理好的缓冲区到消费者队列(一般由 SurfaceFlinger 作为消费者)。
- 锁定缓冲区:
GraphicBuffer->lockAsync
是通过 Gralloc 的 HAL 接口锁定缓冲区,并获取它的 CPU 地址。- 返回的
void* vaddr
是缓冲区的实际像素数据地址,使用这个地址可以直接操作缓冲区中的像素数据。
- 绘制流程:
- 此函数的核心目的是为 CPU 渲染模式准备一个可供直接写入的后缓冲区,允许应用程序在"锁定"期间直接操作像素数据并提交。
- 注意并发和锁保护:
- 多处使用
Mutex
加锁,保证缓冲区的状态更新是线程安全的。
- 多处使用
这个函数的工作核心是通过 dequeueBuffer
获取后缓冲区,处理前缓冲区的内容恢复,更新脏区域,并最终通过 lockAsync
锁定缓冲区,使得用户可以直接写入像素数据。它是 Android 中软件渲染的重要基础,结合 Gralloc 和 BufferQueue 实现内存和缓冲区管理。
下面仔细看几个关键步骤:
4.1、Surface开始申请Buffer:
也就是从dequeueBuffer
开始看:
virtual status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height,PixelFormat format, uint64_t usage, uint64_t* outBufferAge,FrameEventHistoryDelta* outTimestamps) {Parcel data, reply;bool getFrameTimestamps = (outTimestamps != nullptr);// 组织好数据data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());data.writeUint32(width);data.writeUint32(height);data.writeInt32(static_cast<int32_t>(format));data.writeUint64(usage);data.writeBool(getFrameTimestamps);// 发起远程调用status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply);if (result != NO_ERROR) {return result;}*buf = reply.readInt32();*fence = new Fence();result = reply.read(**fence);if (result != NO_ERROR) {fence->clear();return result;}if (outBufferAge) {result = reply.readUint64(outBufferAge);} else {// Read the value even if outBufferAge is nullptr:uint64_t bufferAge;result = reply.readUint64(&bufferAge);}if (result != NO_ERROR) {ALOGE("IGBP::dequeueBuffer failed to read buffer age: %d", result);return result;}if (getFrameTimestamps) {result = reply.read(*outTimestamps);if (result != NO_ERROR) {ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d",result);return result;}}result = reply.readInt32();return result;}
其实主要就是通过remote()->transact
发起了远程调用,导致SF的allocBuffer
被调用,dequeueBuffer得到的(也就是第一个参数)是一个int类型的buf,就是mSlot数组的下标;,具体的,我们去看看SF如何处理这个DEQUEUE_BUFFER
。
调用栈:
Surface::dequeueBuffer ->
mGraphicBufferProducer->
dequeueBuffer ->
dequeueBuffer( native\libs\gui\IGraphicBufferProducer.cpp) ->
remote()->transact -> (发起binder远程调用)
4.2、SurfaceFlinger分配Buffer:
最终会来到SF的BnGraphicBufferProducer::onTransact()
当中,SF收到请求之后呢,也会去自己本地的mSlots[64]里面看看有没有余额,没有的话通过Gralloc模块申请(最终其实通过ashmem申请匿名内存)并返回fd;
status_t BnGraphicBufferProducer::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{switch(code) {// ...// 申请buffercase DEQUEUE_BUFFER: {// 读出里面的参数CHECK_INTERFACE(IGraphicBufferProducer, data, reply);uint32_t width = data.readUint32();uint32_t height = data.readUint32();PixelFormat format = static_cast<PixelFormat>(data.readInt32());uint64_t usage = data.readUint64();uint64_t bufferAge = 0;bool getTimestamps = data.readBool();int buf = 0;sp<Fence> fence = Fence::NO_FENCE;FrameEventHistoryDelta frameTimestamps;// 调用派生类 BufferQueueProducer 的 dequeueBufferint result = dequeueBuffer(&buf, &fence, width, height, format, usage, &bufferAge,getTimestamps ? &frameTimestamps : nullptr);if (fence == nullptr) {ALOGE("dequeueBuffer returned a NULL fence, setting to Fence::NO_FENCE");fence = Fence::NO_FENCE;}reply->writeInt32(buf);reply->write(*fence);reply->writeUint64(bufferAge);if (getTimestamps) {reply->write(frameTimestamps);}reply->writeInt32(result);return NO_ERROR;}// 。。。
}
我注释了,最终会调用BufferQueueProducer::dequeueBuffer
,最终得到的Buffer通过buf返回,注意,这个buf是一个索引,我们去看看:
BufferQueueProducer::dequeueBuffer
它是在生产者(Producer,如 OpenGL、CPU 渲染等)侧被调用,用于从 BufferQueue 中获取一个空闲的缓冲区(Buffer
),供生产者进行写入或渲染操作。
主要执行逻辑:
- 确认 BufferQueue 处于连接且未被抛弃(abandoned)的状态。
- 尝试获取一个空闲的缓冲区槽(Slot)。
- 如果缓冲区需要重新分配(大小或格式不匹配),则执行缓冲区的分配操作。
- 返回这个缓冲区的索引和相关同步信息,供生产者进行写入。
1)函数签名:
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, // [输出] 获取到的缓冲区槽位索引sp<android::Fence>* outFence,// [输出] 同步 Fence,确保缓冲区安全可用uint32_t width, // [输入] 期望的缓冲区宽度uint32_t height, // [输入] 期望的缓冲区高度PixelFormat format, // [输入] 缓冲区的像素格式uint64_t usage, // [输入] 缓冲区的用法标志(如 GPU 访问、CPU 写入)uint64_t* outBufferAge, // [输出] 缓冲区的“年龄”,用于增量更新FrameEventHistoryDelta* outTimestamps // [输出] 帧时间戳信息
)
- 这是生产者从 BufferQueue 中获取缓冲区的接口。
- 参数包括缓冲区的尺寸、格式、用法等信息,同时函数会通过
outSlot
返回获得的槽位,outFence
返回一个同步对象确保缓冲区可以被安全访问。
2)完整代码:
/*** 父类 BnGraphicBufferProducer 处理 surface 申请buffer时候调用*/
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,uint32_t width, uint32_t height, PixelFormat format,uint64_t usage, uint64_t* outBufferAge,FrameEventHistoryDelta* outTimestamps) {ATRACE_CALL();{ // Autolock scopestd::lock_guard<std::mutex> lock(mCore->mMutex);mConsumerName = mCore->mConsumerName;if (mCore->mIsAbandoned) {BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");return NO_INIT;}if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {BQ_LOGE("dequeueBuffer: BufferQueue has no connected producer");return NO_INIT;}} // Autolock scopeBQ_LOGV("dequeueBuffer: w=%u h=%u format=%#x, usage=%#" PRIx64, width, height, format, usage);if ((width && !height) || (!width && height)) {BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);return BAD_VALUE;}status_t returnFlags = NO_ERROR;EGLDisplay eglDisplay = EGL_NO_DISPLAY;EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;bool attachedByConsumer = false;{ // Autolock scopestd::unique_lock<std::mutex> lock(mCore->mMutex);// If we don't have a free buffer, but we are currently allocating, we wait until allocation// is finished such that we don't allocate in parallel.if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {mDequeueWaitingForAllocation = true;mCore->waitWhileAllocatingLocked(lock);mDequeueWaitingForAllocation = false;mDequeueWaitingForAllocationCondition.notify_all();}if (format == 0) {format = mCore->mDefaultBufferFormat;}// Enable the usage bits the consumer requestedusage |= mCore->mConsumerUsageBits;const bool useDefaultSize = !width && !height;if (useDefaultSize) {width = mCore->mDefaultWidth;height = mCore->mDefaultHeight;}int found = BufferItem::INVALID_BUFFER_SLOT;while (found == BufferItem::INVALID_BUFFER_SLOT) {// 1.查看mSlots有没有空闲项;status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);if (status != NO_ERROR) {return status;}// This should not happenif (found == BufferQueueCore::INVALID_BUFFER_SLOT) {BQ_LOGE("dequeueBuffer: no available buffer slots");return -EBUSY;}const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);// If we are not allowed to allocate new buffers,// waitForFreeSlotThenRelock must have returned a slot containing a// buffer. If this buffer would require reallocation to meet the// requested attributes, we free it and attempt to get another one.// 如果不允许分配新缓冲区,// waitForFreeSlotThenRelock 必须返回一个包含// buffer的slot。如果此buffer需要重新分配才能满足// 请求的属性,我们将释放它并尝试获取另一个。if (!mCore->mAllowAllocation) {// 判断是否需要重新分配bufferif (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {if (mCore->mSharedBufferSlot == found) {BQ_LOGE("dequeueBuffer: cannot re-allocate a sharedbuffer");return BAD_VALUE;}mCore->mFreeSlots.insert(found);mCore->clearBufferSlotLocked(found);found = BufferItem::INVALID_BUFFER_SLOT;continue;}}}const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);if (mCore->mSharedBufferSlot == found &&buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {BQ_LOGE("dequeueBuffer: cannot re-allocate a shared""buffer");return BAD_VALUE;}if (mCore->mSharedBufferSlot != found) {mCore->mActiveBuffers.insert(found);}*outSlot = found;ATRACE_BUFFER_INDEX(found);attachedByConsumer = mSlots[found].mNeedsReallocation;mSlots[found].mNeedsReallocation = false;mSlots[found].mBufferState.dequeue();// 2.如果buffer为null,或者需要重新分配if ((buffer == nullptr) ||buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)){mSlots[found].mAcquireCalled = false;mSlots[found].mGraphicBuffer = nullptr;mSlots[found].mRequestBufferCalled = false;mSlots[found].mEglDisplay = EGL_NO_DISPLAY;mSlots[found].mEglFence = EGL_NO_SYNC_KHR;mSlots[found].mFence = Fence::NO_FENCE;mCore->mBufferAge = 0;mCore->mIsAllocating = true;// 3. 将flag设置为需要重新分配returnFlags |= BUFFER_NEEDS_REALLOCATION;} else {// We add 1 because that will be the frame number when this buffer// is queuedmCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;}BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64,mCore->mBufferAge);if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) {BQ_LOGE("dequeueBuffer: about to return a NULL fence - ""slot=%d w=%d h=%d format=%u",found, buffer->width, buffer->height, buffer->format);}eglDisplay = mSlots[found].mEglDisplay;eglFence = mSlots[found].mEglFence;// Don't return a fence in shared buffer mode, except for the first// frame.*outFence = (mCore->mSharedBufferMode &&mCore->mSharedBufferSlot == found) ?Fence::NO_FENCE : mSlots[found].mFence;mSlots[found].mEglFence = EGL_NO_SYNC_KHR;mSlots[found].mFence = Fence::NO_FENCE;// If shared buffer mode has just been enabled, cache the slot of the// first buffer that is dequeued and mark it as the shared buffer.if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==BufferQueueCore::INVALID_BUFFER_SLOT) {mCore->mSharedBufferSlot = found;mSlots[found].mBufferState.mShared = true;}} // Autolock scope// 4.如果需要重新分配,那么进行分配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()});status_t error = graphicBuffer->initCheck();{ // Autolock scopestd::lock_guard<std::mutex> lock(mCore->mMutex);if (error == NO_ERROR && !mCore->mIsAbandoned) {graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);mSlots[*outSlot].mGraphicBuffer = graphicBuffer;}mCore->mIsAllocating = false;mCore->mIsAllocatingCondition.notify_all();if (error != NO_ERROR) {mCore->mFreeSlots.insert(*outSlot);mCore->clearBufferSlotLocked(*outSlot);BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");return error;}if (mCore->mIsAbandoned) {mCore->mFreeSlots.insert(*outSlot);mCore->clearBufferSlotLocked(*outSlot);BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");return NO_INIT;}VALIDATE_CONSISTENCY();} // Autolock scope}if (attachedByConsumer) {returnFlags |= BUFFER_NEEDS_REALLOCATION;}if (eglFence != EGL_NO_SYNC_KHR) {EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,1000000000);// If something goes wrong, log the error, but return the buffer without// synchronizing access to it. It's too late at this point to abort the// dequeue operation.if (result == EGL_FALSE) {BQ_LOGE("dequeueBuffer: error %#x waiting for fence",eglGetError());} else if (result == EGL_TIMEOUT_EXPIRED_KHR) {BQ_LOGE("dequeueBuffer: timeout waiting for fence");}eglDestroySyncKHR(eglDisplay, eglFence);}BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",*outSlot,mSlots[*outSlot].mFrameNumber,mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);if (outBufferAge) {*outBufferAge = mCore->mBufferAge;}addAndGetFrameTimestamps(nullptr, outTimestamps);return returnFlags;
}
关键部分已经加注释了,如果需要重新分配,就会调用sp<GraphicBuffer> graphicBuffer = new GraphicBuffer
;分配的Buffer保存到了mSlots[*outSlot].mGraphicBuffer
。这个outSlot其实就是一个buffer的索引值。下面分开看:
3)初步检查状态:
{ // Autolock scopestd::lock_guard<std::mutex> lock(mCore->mMutex); // 锁定核心互斥量,防止并发访问// 如果队列已被消费者弃用(abandoned),返回 NO_INIT 错误if (mCore->mIsAbandoned) {return NO_INIT;}// 如果生产者未连接到 BufferQueue,同样返回 NO_INIT 错误if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {return NO_INIT;}
} // Autolock scope
解释:
- 通过
mCore->mMutex
锁定核心数据结构,确保线程安全。 - 检查 BufferQueue 当前的状态:
- 如果被消费者(Consumer,例如 SurfaceFlinger)抛弃,说明队列不可用。
- 如果没有生产者连接到 BufferQueue,则无法执行任何缓冲区分配操作。
4)处理延迟分配的情况:
bool attachedByConsumer = false;
{ // Autolock scopestd::unique_lock<std::mutex> lock(mCore->mMutex);// 如果没有可用的缓冲区并且当前处于分配过程,等待分配完成后继续执行。if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {mDequeueWaitingForAllocation = true;mCore->waitWhileAllocatingLocked(lock); // 等待缓冲区分配完成mDequeueWaitingForAllocation = false;mDequeueWaitingForAllocationCondition.notify_all();}
解释:
- 检查是否有空闲缓冲区:
- 如果
mFreeBuffers
为空,且当前正在分配缓冲区(mIsAllocating
为真),将进入等待状态。
- 如果
- 调用
waitWhileAllocatingLocked
,当分配完成后继续查找缓冲区。
5)缓冲区格式与尺寸的默认处理:
if (format == 0) {format = mCore->mDefaultBufferFormat; // 如果未指定格式,则使用默认格式}usage |= mCore->mConsumerUsageBits; // 合并消费者使用标志const bool useDefaultSize = !width && !height; // 如果宽高未指定,则采用默认值if (useDefaultSize) {width = mCore->mDefaultWidth;height = mCore->mDefaultHeight;}
解释:
- 默认值处理:
- 如果未明确指定格式 (
format == 0
),使用 BufferQueue 默认的格式。 - 如果宽度或高度为
0
,则采用 BufferQueue 默认尺寸。
- 如果未明确指定格式 (
usage
参数会合并消费者的使用标志。例如,GPU 的某些用法可能是由消费者定义的。
6) 查找空闲槽:
int found = BufferItem::INVALID_BUFFER_SLOT; // 初始化为无效槽位while (found == BufferItem::INVALID_BUFFER_SLOT) {// 尝试查找一个空闲的缓冲区槽status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);if (status != NO_ERROR) {return status; // 如果找不到空闲槽,返回状态}// 防御性检查:确保槽位有效if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {return -EBUSY; // 没有可用槽位}}
解释:
-
调用
waitForFreeSlotThenRelock
查找可用槽位。如果没有空闲的缓冲区槽,可能会进入阻塞状态等待。 -
若最终找到一个无效槽(极端情况),返回
-EBUSY
错误。
7)缓冲区重新分配检查:
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);// 检查缓冲区是否需要重新分配if ((buffer == nullptr) ||buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {mSlots[found].mAcquireCalled = false;mSlots[found].mGraphicBuffer = nullptr;mCore->mBufferAge = 0; // 重设缓冲区年龄returnFlags |= BUFFER_NEEDS_REALLOCATION; // 设置标志表明需要重新分配}
解释:
- 检查当前槽位的 Buffer 是否需要重新分配:
- 如果缓冲区不存在(
buffer == nullptr
)。 - 如果现有缓冲区的尺寸、格式或使用标志与期望值不匹配,则需要重新分配。
- 如果缓冲区不存在(
- 如果需要重新分配,设置
BUFFER_NEEDS_REALLOCATION
标志,之后会触发分配流程。
8)重新分配缓冲区:
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,{mConsumerName.string(), mConsumerName.size()});status_t error = graphicBuffer->initCheck();...if (error != NO_ERROR) {mCore->mFreeSlots.insert(*outSlot); // 分配失败,将槽位标记为“空闲”return error;}
}
解释:
- 如果需要重新分配缓冲区:
- 创建新的
GraphicBuffer
,并传入尺寸、格式和用法标志。 - 初始化后检查创建是否成功。
- 创建新的
- 若分配失败,将槽位重新标记为空闲,返回错误状态。
看看内部如何分配Buffer:
其中new
之后,构造函数里面会调用Gralloc模块的AShmem分配Buffer:
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,uint32_t inLayerCount, uint64_t inUsage, std::string requestorName): GraphicBuffer() {// 里面会分配buffermInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,std::move(requestorName));
}
再看看
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,std::string requestorName)
{// 打开HALGraphicBufferAllocator& allocator = GraphicBufferAllocator::get();uint32_t outStride = 0;// 申请一个Allocator并且进行分配buffer// 分配之后会得到一个fd,使用handle封装fd,最终返回应用程序的是handlestatus_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;
}
就是调用了HAL模块去申请了个Buffer,并返回了handle;
9)返回结果:
*outSlot = found; // 返回找到的缓冲区槽位
*outFence = mSlots[found].mFence; // 返回同步 Fence
if (outBufferAge) {*outBufferAge = mCore->mBufferAge; // 返回缓冲区年龄
}
return returnFlags;
解释:
- 将找到的槽位索引返回给调用者。
- 返回同步 Fence,用于协调缓冲区访问安全性。
- 同时返回缓冲区的“年龄”信息,供生产者在绘制增量更新时使用。
10)小结:
BufferQueueProducer::dequeueBuffer
的工作流程:
- 检查 BufferQueue 是否处于有效状态。
- 查找一个空闲的缓冲区槽,检查是否满足需求。
- 如果需要,对缓冲区执行重新分配。
- 返回缓冲区的槽位、同步信息等结果,供生产者使用。
4.2、获取Buffer:
1)requestBuffer:
SF会将Buffer索引返回,我们看下的dequeueBuffer
之后:
// 如果result是 BUFFER_NEEDS_REALLOCATION 置为,说明SF帮我们重新分配了bufferif ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {if (mReportRemovedBuffers && (gbuf != nullptr)) {mRemovedBuffers.push_back(gbuf);}// 那么,调用request去获取重新分配的bufferresult = mGraphicBufferProducer->requestBuffer(buf, &gbuf);if (result != NO_ERROR) {ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);mGraphicBufferProducer->cancelBuffer(buf, fence);return result;}}
也就是说,如果requestBuffer
获取的结果result置位了IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION
说明SF已经帮我们重新分配了一个Buffer,因此,我们还需要调用requestBuffer
获取这个新buffer。记住,requestBuffer
不会去重新分配Buffer,只是相当于确认用这个Buffer。我们先看代码,后面再详细分析:
先执行代理类BpGraphicBufferProducer
的方法:
// 获取SF给我们分配的buffervirtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {// 构造好参数Parcel data, reply;data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());data.writeInt32(bufferIdx);// 发起远程调用status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);if (result != NO_ERROR) {return result;}bool nonNull = reply.readInt32();if (nonNull) {// 创建GB对象buf*buf = new GraphicBuffer();// 通过reply来构造这个对象bufresult = reply.read(**buf);if(result != NO_ERROR) {(*buf).clear();return result;}}result = reply.readInt32();return result;}
这里 BpGraphicBufferProducer
读取SF通过Binder发送过来的 Parcel
对象 reply
,先创建了一个自己 GraphicBuffer
对象,然后通过reply.read读取具体的内容,内容就是fd'
。
SurfaceFlinger 进程:
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {ATRACE_CALL();BQ_LOGV("requestBuffer: slot %d", slot);std::lock_guard<std::mutex> lock(mCore->mMutex);if (mCore->mIsAbandoned) {BQ_LOGE("requestBuffer: BufferQueue has been abandoned");return NO_INIT;}if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {BQ_LOGE("requestBuffer: BufferQueue has no connected producer");return NO_INIT;}if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",slot, BufferQueueDefs::NUM_BUFFER_SLOTS);return BAD_VALUE;} else if (!mSlots[slot].mBufferState.isDequeued()) {BQ_LOGE("requestBuffer: slot %d is not owned by the producer ""(state = %s)", slot, mSlots[slot].mBufferState.string());return BAD_VALUE;}mSlots[slot].mRequestBufferCalled = true;*buf = mSlots[slot].mGraphicBuffer;return NO_ERROR;
}
看到没有,其实主要主要就是mSlots[slot].mRequestBufferCalled = true;
这一句,其他都是对Buffer做一些校验,看这个Buffer是否和自己期望的一样。
执行之后,我们就持有了SF分配的Buffer的fd;
2)dequeueBuffer和
requestBuffer:
dequeueBuffer
和requestBuffer
的区别:dequeueBuffer
: 用于获取一个空闲槽位,并确保槽位内有可用的缓冲区。如果需要,会预先分配(new GraphicBuffer
)。requestBuffer
: 确认并获取槽位中实际的缓冲区对象,供生产者正式使用。
- 为什么分配了缓冲区还需要
requestBuffer
?new GraphicBuffer
只是分配了缓冲区,但不会直接传递到生产者的上下文中。requestBuffer
将分配的缓冲区对象交给生产者进行具体的读取或写入操作。
- 使用流程:
dequeueBuffer
找槽 ->requestBuffer
获取缓冲区对象 -> 使用该 Buffer ->queueBuffer
提交。
3)应用使用Buffer:
根据之前的代码:
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {*buffer = gbuf.get();if (mSharedBufferMode && mAutoRefresh) {mSharedBufferSlot = buf;mSharedBufferHasBeenQueued = false;} else if (mSharedBufferSlot == buf) {mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;mSharedBufferHasBeenQueued = false;}
}
可以看出应该用层通过Surface类得到的Buffer索引最终都放到mSharedBufferSlot
这个类成员变量了,同时以出参*buffer
告诉了调用者这个buffer的’fd’。
然后就到了我们调用者,也就是Surface::lock
函数这里:
(这函数老长了,我删了不重要的)
status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{// ... 删除非关键代码// 用于保存 dequeueBuffer 获取到的缓冲区ANativeWindowBuffer* out;// 调用 GraphicBufferProducer 的 dequeueBuffer 获取生产者(Producer)队列中的后缓冲区status_t err = dequeueBuffer(&out, &fenceFd);if (err == NO_ERROR) {// 此时获取到一个合法的 backBuffer(后缓冲区)sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); // 包装为 GraphicBuffer 对象// ... 删除了处理脏区域// ... 删了优化和校验部分// 锁定缓冲区并获取指针(vaddr)// 调用 GraphicBuffer 的 lockAsync 来实际锁定缓冲区并得到内存地址void* vaddr;status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,newDirtyRegion.bounds(), &vaddr, fenceFd);if (res != 0) {err = INVALID_OPERATION;} else {// 锁定成功,保存缓冲区的信息到 outBuffermLockedBuffer = backBuffer;outBuffer->width = backBuffer->width;outBuffer->height = backBuffer->height;outBuffer->stride = backBuffer->stride;outBuffer->format = backBuffer->format;outBuffer->bits = vaddr; // 保存缓冲区 CPU 可写的内存地址}}return err;
}
重点函数就是通过backBuffer->lockAsync
锁定缓冲区,避免其他应用或者SurfaceFlinger进行竞争。这里面备份了更新后缓冲区其实是在前缓冲区基础上部分更新,那部分被我删了,此刻,并不影响理解。
锁定缓冲区:
直接上代码,有三个,都是为了接口更灵活做的内部调用,我们直接看最后一个:
status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd,int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {const Rect lockBounds(width, height);status_t res =lockAsync(inUsage, lockBounds, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);return res;
}status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd,int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);
}status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,const Rect& rect, void** vaddr, int fenceFd,int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {if (rect.left < 0 || rect.right > width ||rect.top < 0 || rect.bottom > height) {ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",rect.left, rect.top, rect.right, rect.bottom,width, height);return BAD_VALUE;}status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect,vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);return res;
}
最后一个对显示区域做了校验,然后直接调用了lockAsync
:
status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage,uint64_t consumerUsage, const Rect& bounds, void** vaddr,int fenceFd, int32_t* outBytesPerPixel,int32_t* outBytesPerStride) {ATRACE_CALL();const uint64_t usage = static_cast<uint64_t>(android_convertGralloc1To0Usage(producerUsage, consumerUsage));// mMapper是一个GrallocMapper变量,实现者可以是2.0或者3.0return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel,outBytesPerStride);
}
我们看下3.0:
status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,int acquireFence, void** outData, int32_t* outBytesPerPixel,int32_t* outBytesPerStride) const {auto buffer = const_cast<native_handle_t*>(bufferHandle);IMapper::Rect accessRegion = sGralloc3Rect(bounds);// put acquireFence in a hidl_handlehardware::hidl_handle acquireFenceHandle;NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);if (acquireFence >= 0) {auto h = native_handle_init(acquireFenceStorage, 1, 0);h->data[0] = acquireFence;acquireFenceHandle = h;}Error error;auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,[&](const auto& tmpError, const auto& tmpData,const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) {error = tmpError;if (error != Error::NONE) {return;}*outData = tmpData;if (outBytesPerPixel) {*outBytesPerPixel = tmpBytesPerPixel;}if (outBytesPerStride) {*outBytesPerStride = tmpBytesPerStride;}});// we own acquireFence even on errorsif (acquireFence >= 0) {close(acquireFence);}error = (ret.isOk()) ? error : kTransactionError;ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);return static_cast<status_t>(error);
}
这个成员变量是 Gralloc3Mapper
类持有的一个 Gralloc 3.0 版本 IMapper
接口的智能指针。通过这个接口指针,Gralloc3Mapper
可以调用 Gralloc 硬件驱动程序提供的各种图形缓冲区操作功能。驱动和Gralloc之间已经规定了**HIDL (Hardware Interface Definition Language)**接口的。
五、总结:
本文主要介绍了,应用层如何申请并获得SurfaceFlinger的Buffer,省略了中间Binder部分,不熟悉的可以看之前的文章,还有,最重要的是返回的都是buffer在mSlots数组中的索引,不是傻乎乎拷贝数据。