Android SurfaceView 二级双缓冲机制深入分析: 从应用到驱动

本文统简地分析了 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 个,所以系统上是多缓冲机制的)。

缓冲区的生命周期大致如下:

  1. Dequeue(出队)

上层(如 SurfaceView)调用 lockCanvas() → 系统从 mSlots[] 中找一个空闲 buffer 返回 → 如果没有可用,就 block 或失败(除非允许 allocate)。

  1. 绘制(CPU/GPU)

应用在这个 GraphicBuffer 上绘图。

  1. Queue(入队)

调用 unlockCanvasAndPost() → 把绘制完的 buffer 送回 BufferQueue,由 SurfaceFlinger 等消费者接管。

  1. 显示(或合成)

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.cppION 分配器层,是 HAL 实现
libgralloc/mapper.cpp:100GraphicBufferMapper::lock() 实现

五、总结

  • Android 通过 BufferQueue 实现双缓冲
  • buffer 最终是由 Gralloc HAL 分配显实存储
  • 用 mmap 将 fd 映射成进程地址
  • 通过 binder + fd 进行跨进程共享

这些设计使 Android 的 UI 控制有着极高效率和扩展性,也是 SurfaceFlinger / SurfaceView 可靠性操作的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值