本文统简地分析了 Android 中 SurfaceView
的双缓冲机制实现。我们会一路跑通从 App 端 SurfaceHolder.lockCanvas()
到最终 mmap 显示 buffer 的全过程,并配合 AOSP 和硬件平台代码进行分析。
一、基础缓冲模型
Android 使用 Producer-Consumer 模型,由 App 端的 Surface
作为 BufferQueue 的 producer,由 SurfaceFlinger 或 GPU 作为 consumer,互通 buffer。
App 端:
Canvas canvas = holder.lockCanvas(); // 绘图 buffer
...// 绘图
holder.unlockCanvasAndPost(canvas); // 推送 buffer
从应用角度看,在这个过程中,Android 启用了双缓冲机制:
- 当前绘图的 buffer
- 上一帧显示的 buffer
这个机制保证了 UI 滑动和性能的稳定性。
但值得说明的是:实际上 Android 底层的 Surface 在系统级别支持多缓冲的,不止双缓冲,但在 SurfaceView 开发中我们主要操作的是“逻辑上”的双缓冲行为。后面我们会分析到。
二、Surface.lockCanvas 过程分析
Canvas canvas = holder.lockCanvas();最终会调用status_t Surface::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)。canvas应该是对应outBuffer。然后在这个函数里面调用int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd)
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
dqInput.height, dqInput.format,
dqInput.usage, &mBufferAge,
dqInput.getTimestamps ?
&frameTimestamps : nullptr);
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
// 需要重新分
if (mReportRemovedBuffers && (gbuf != nullptr)) {
mRemovedBuffers.push_back(gbuf);
}
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
mGraphicBufferProducer->cancelBuffer(buf, fence);
return result;
}
}
}
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) {
int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
// waitForFreeSlotThenRelock: 先从FreeBuffer找,没有再从FreeSlot找
status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
...
}
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if (mCore->mSharedBufferSlot != found) {
mCore->mActiveBuffers.insert(found);
}
*outSlot = found;
attachedByConsumer = mSlots[found].mNeedsReallocation;
mSlots[found].mNeedsReallocation = false;
mSlots[found].mBufferState.dequeue();
if (need realloc) {
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
status_t error = graphicBuffer->initCheck();
}
...
}
整理下流程:
Surface::lock(ANativeWindow_Buffer*, ARect*)
→ Surface::dequeueBuffer()
→ BufferQueueProducer::dequeueBuffer()
→ waitForFreeSlotThenRelock() // 找到可用 buffer slot
→ mSlots[slot].mGraphicBuffer // 返回 GraphicBuffer
→ GraphicBuffer::initWithSize() // 需新分配时
双缓冲/多缓冲在这个链条中的体现
mSlots[]数组其实就是 缓冲池(buffer pool) ,默认支持 3 个缓冲区(不一定固定是 2 个,所以系统上是多缓冲机制的)。
缓冲区的生命周期大致如下:
- Dequeue(出队)
上层(如 SurfaceView
)调用 lockCanvas()
→ 系统从 mSlots[]
中找一个空闲 buffer 返回 → 如果没有可用,就 block 或失败(除非允许 allocate)。
- 绘制(CPU/GPU)
应用在这个 GraphicBuffer
上绘图。
- Queue(入队)
调用 unlockCanvasAndPost()
→ 把绘制完的 buffer 送回 BufferQueue
,由 SurfaceFlinger 等消费者接管。
- 显示(或合成)
SurfaceFlinger 作为 consumer,从队列里拿到 buffer → Composite → 显示。
三、GraphicBuffer 实际分配
GraphicBuffer
是一个 Android Framework 层对 native graphic memory 的封装。
GraphicBuffer::initWithSize()
→ GraphicBufferAllocator::allocate()
→ GrallocXAllocator::allocate()
→ mAllocator->allocate() // IAllocator (HIDL)
相关代码如下
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);
...
}
}
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) {
return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
true);
}
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());
}
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) {
ATRACE_CALL();
....
// std::unique_ptr<const GrallocAllocator> mAllocator;
status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
stride, handle, importBuffer);
...
}
// mAllocator->allocate应该就进入驱动层了,以Gralloc3Allocator为例:
Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
ALOGW("allocator 3.x is not supported");
return;
}
}
status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool importBuffers) const {
auto ret = mAllocator->allocate(...)...
}
上面的IAllocator::getService()->allocate(...)
就是 Binder 跨进程到 HAL 服务了, 通过 HIDL 获取 Gralloc HAL 服务。这个接口在系统启动时就由 vendor.gralloc
实现注册到了 HAL 服务管理器。
最终会调用硬件平台的 gralloc HAL:
以 QCOM 为例:
gr_ion_alloc.cpp:
addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
ion_alloc_fd()
分配显实 memory- 将 fd 用 mmap 映射成用户端地址
- 返回这个地址给
GraphicBuffer
四、mmap 问题分析
给定 platform (如 msm8909) 下调用 mmap 分析:
$ grep mmap -rn ./hardware/qcom/display/msm8909
./libqdutils/qdMetaData.cpp:55: void *base = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED,
./libqdutils/qdMetaData.cpp:58: ALOGE("%s: metadata mmap failed - handle:%p fd: %d err: %s",
./gralloc/gr_ion_alloc.cpp:132: addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
./libgralloc/ionalloc.cpp:103: base = mmap(0, ionAllocData.len, PROT_READ|PROT_WRITE,
./libgralloc/ionalloc.cpp:153: base = mmap(0, size, PROT_READ| PROT_WRITE,
./libgralloc/framebuffer.cpp:331: void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
./libgralloc/mapper.cpp:70: ALOGE("Could not mmap metadata for handle %p, fd=%d (%s)",
./libgralloc/mapper.cpp:100: ALOGE("Could not mmap handle %p, fd=%d (%s)",
./sdm/libs/hwc2/hwc_color_manager.cpp:226: void *buffer = mmap(NULL, buffer_info.alloc_buffer_info.size, PROT_READ | PROT_WRITE,
./sdm/libs/hwc2/hwc_color_manager.cpp:230: DLOGE("mmap failed. err = %d", errno);
./sdm/libs/hwc2/hwc_display_external_test.cpp:223: char *buffer = reinterpret_cast<char *>(mmap(NULL, buffer_info_.alloc_buffer_info.size,
./sdm/libs/hwc2/hwc_display_external_test.cpp:227: DLOGW("mmap failed. err = %d", errno);
./sdm/libs/hwc2/hwc_display_external_test.cpp:328: uint8_t *buffer = reinterpret_cast<uint8_t *>(mmap(NULL, buffer_info_.alloc_buffer_info.size,
./sdm/libs/hwc2/hwc_display_external_test.cpp:332: DLOGE("mmap failed. err = %d", errno);
./sdm/libs/hwc2/hwc_display_primary.cpp:524: void *buffer = mmap(NULL, output_buffer_info_.alloc_buffer_info.size, PROT_READ | PROT_WRITE,
./sdm/libs/hwc2/hwc_display_primary.cpp:528: DLOGE("mmap failed with err %d", errno);
./sdm/libs/hwc/hwc_color_manager.cpp:375: void *buffer = mmap(NULL, buffer_info.alloc_buffer_info.size,
./sdm/libs/hwc/hwc_color_manager.cpp:380: DLOGE("mmap failed. err = %d", errno);
./sdm/libs/hwc/hwc_display_external_test.cpp:230: char *buffer = reinterpret_cast<char *>(mmap(NULL, buffer_info_.alloc_buffer_info.size,
./sdm/libs/hwc/hwc_display_external_test.cpp:234: DLOGW("mmap failed. err = %d", errno);
./sdm/libs/hwc/hwc_display_external_test.cpp:335: uint8_t *buffer = reinterpret_cast<uint8_t *>(mmap(NULL, buffer_info_.alloc_buffer_info.size,
./sdm/libs/hwc/hwc_display_external_test.cpp:339: DLOGE("mmap failed. err = %d", errno);
./sdm/libs/hwc/hwc_display_primary.cpp:484: void *buffer = mmap(NULL, output_buffer_info_.alloc_buffer_info.size,
./sdm/libs/hwc/hwc_display_primary.cpp:489: DLOGE("mmap failed with err %d", errno);
上面已经接近看到真正显存映射到用户空间的「关键点」了。有兴趣可以看看哪些值得深入阅读。
关键代码:
文件 | mmap 说明 |
---|---|
gralloc/gr_ion_alloc.cpp:132 | 主分配 + mmap 场景 |
libgralloc/ionalloc.cpp | ION 分配器层,是 HAL 实现 |
libgralloc/mapper.cpp:100 | GraphicBufferMapper::lock() 实现 |
五、总结
- Android 通过 BufferQueue 实现双缓冲
- buffer 最终是由 Gralloc HAL 分配显实存储
- 用 mmap 将 fd 映射成进程地址
- 通过 binder + fd 进行跨进程共享
这些设计使 Android 的 UI 控制有着极高效率和扩展性,也是 SurfaceFlinger / SurfaceView 可靠性操作的基础。