Android图形显示系统6 图像缓冲区(下)

2023-11-06

一 概述

我们再次回顾下上一篇文章 Android图形显示系统5 图像缓冲区(上) 描述的图像缓冲区。
在这里插入图片描述
1.Android 图形缓冲区由哪些部分组成

Android 的图形缓冲区由 Surface,BufferQueue,Layer,GraphicBuffer 四部分组成。BufferQueue 中的 slots 数组最多能存储 64 个 GraphicBuffer,而 GraphicBuffer 就是真正被分配内存,并能存储图形数据的缓冲区

2.Surface 和 Layer 分别是什么

Surface 是提供给图形生产者控制缓冲区的类,Surface 持有 GraphicBufferProducer,简称 gbp,gbp 专门用来创建或获取可用的 GraphicBuffer 以及提交绘制后的 GraphicBuffer
Layer 是提供给图像消费者获取缓冲区的类,Layer 持有 GraphicBufferConsumer,简称 gbc,通过 gbc 用来获取和释放 GraphicBuffer

3.他们是怎么关联起来的

Surface 和 Layer 通过 BufferQueue 关联起来,Surface 持有 BufferQueue 中的 gbp,Layer 持有 BufferQueue 中的 gbc,gbp 和 gbc 的 GraphicBuffer 都存储在 GraphicBufferCore 的 slots 数组中

4.如何创建、获取以及使用缓冲区

Surface 通过调用 gbp 的 dequeue 函数获取 GraphicBuffer,调用 queue 函数提交使用完毕的 GraphicBuffer
Layer 通过调用 gbc 的 acquire 函数获取有数据的 GraphicBuffer,调用 release 释放 GraphicBuffer

总结:当我们想要绘制图像时,需要创建 Surface 和 Layer,图像生产者如 Skia,OpenGL 通过 Surface 调用 dequeue 函数获取一块缓冲区 GraphicBuffer,有了这块缓冲区,就可以在缓冲区上绘制图像了,当绘制完毕后,通过 Surface 调用 queue 函数,将 GraphicBuffer 归还到 BufferQueue。之后,Vsync 通知图像消费者 SurfaceFlinger 调用 Layer 的 acquire 函数,获取绘制好内容的 GraphicBuffer 进行合成与处理,处理完毕后通过 release 函数释放这块缓冲区。

接上一篇文章,我们继续分析 BufferQueue 的创建。

二 BufferQueue的创建

在前面分析 Layer 的创建时,有提到会在 Layer 的初始化函数 onFirstRef 中会创建 BufferQueue,onFirstRef 函数实现如下

/frameworks/native/services/surfaceflinger/Layer.cpp

void Layer::onFirstRef() {
    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer, true);
    mProducer = new MonitoredProducer(producer, mFlinger, this);
    mSurfaceFlingerConsumer = 
        new SurfaceFlingerConsumer(consumer, mTextureName, this);
    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
    mSurfaceFlingerConsumer->setContentsChangedListener(this);
    mSurfaceFlingerConsumer->setName(mName);

    if (mFlinger->isLayerTripleBufferingDisabled()) {
        mProducer->setMaxDequeuedBufferCount(2);
    }
    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
    updateTransformHint(hw);
}

onFirstRef 函数中关键的一步就是调用 createBufferQueue 创建 BufferQueue,它的实现如下

/frameworks/native/libs/gui/BufferQueue.cpp

void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
        sp<IGraphicBufferConsumer>* outConsumer,
        bool consumerIsSurfaceFlinger) {

    sp<BufferQueueCore> core(new BufferQueueCore());    
    sp<IGraphicBufferProducer> producer(
        new BufferQueueProducer(core, consumerIsSurfaceFlinger));
    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
  
    *outProducer = producer;
    *outConsumer = consumer;
}

createBufferQueue 做了三件事情

  • 创建 BufferQueueCore
  • 创建 BufferQueueProducer
  • 创建 BufferQueueConsumer

下面详细讲一下这三件事情

2.1 BufferQueueCore

在概述里面提到过,BufferQueueCore 拥有一个 slots 数组用来存储 GraphicBuffer,并且最多可能存放 64 个 GraphicBuffer。先看看 BufferQueueCore 的头文件。

/frameworks/native/include/gui/BufferQueueCore.h

namespace android {

class BufferQueueCore : public virtual RefBase {

    friend class BufferQueueProducer;
    friend class BufferQueueConsumer;
    ......
private:    
    ......
    BufferQueueDefs::SlotsType mSlots;
    Fifo mQueue;
    std::set<int> mFreeSlots;
    std::list<int> mFreeBuffers;
    std::list<int> mUnusedSlots;
    std::set<int> mActiveBuffers;
    ......
}; 
} 
#endif

从 BufferQueueCore 的头文件可以看到,BufferQueueCore 除了持有 BufferQueueProducer,BufferQueueConsumer 和 mSlots,还持有很多其他的数据,如 mQueue,mFreeSlots,mFreeBuffers 等。下面介绍一下关键的几个成员变量。

  • mSlots 就是一个 BufferSlot 数组,大小为 NUM_BUFFER_SLOTS,也就是 64 个,BufferSlot 主要是用来绑定 GraphicBuffer 的,一个 BufferSlot 绑定一个 GraphicBuffer
//文件-->/frameworks/native/include/gui/BufferQueueDefs.h
namespace android {
    class BufferQueueCore;
    namespace BufferQueueDefs {
        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
    } 
} 

//文件-->/frameworks/native/include/ui/BufferQueueDefs.h
namespace android {
    namespace BufferQueueDefs {        
        static constexpr int NUM_BUFFER_SLOTS = 64;
    } 
} 
  • mQueue:以队列的方式存放图像消费者提交的 GraphicBuffer
  • mFreeSlots:代表所有没有绑定 GraphicBuffer 的 BufferSlot 集合
  • mActiveBuffers:代表所有绑定了 GraphicBuffer 的 BufferSlot 集合,并且 BufferSlot 状态不为 FREE
  • mUnusedSlots:代表当前没有使用的 BufferSlot 集合

这里介绍一下 BufferSlot,它的数据结构如下。可以看到 BufferSlot 持有一个 GraphicBuffer,以及 BufferState,还有一些其他的数据。

/frameworks/native/include/gui/BufferSlot.h

struct BufferSlot {
    BufferSlot()
    : mGraphicBuffer(nullptr),
      mBufferState(),
      ……{
    }

    sp<GraphicBuffer> mGraphicBuffer;
    BufferState mBufferState;
    ......
};

BufferState 又分为五种状态,FREE,DEQUEUED,QUEUED,ACQUIRED,SHARED。了解了 BufferSlot 和 BufferState,我们也就能理解 BufferQueueCore 为什么会有这么多存储 GraphicBuffer 的数据结构,如 slots,mFreeSlots,mFreeBuffers 等等,这些数据结构都是为了给 BufferSlot 分类,以便获取 GraphicBuffer 时更加高效。

2.2 BufferQueueProducer

了解了 BufferQueueCore,我们接着看 BufferQueueProducer,在前面概述中提到,Surface 会中持 BufferQueueProducer 的 BP 代理。它的构造函数如下。

/frameworks/native/libs/gui/BufferQueueProducer.cpp

BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
        bool consumerIsSurfaceFlinger) :
    mCore(core),
    mSlots(core->mSlots),
    …… {}

BufferQueueProducer 的构造函数是空方法,在初始化成员变量时,会直接将前面创建好的 BufferQueueCore 和 mSlots 赋值到 BufferQueueProducer 的成员变量中。

我们已经知道,图像生产者通过 dequeue 函数来获取一块可用的 GraphicBuffer,并通过 queue 来归还绘制了数据的 Graphicbuffer。
在这里插入图片描述
那么我们在 BufferQueueProducer 中看看这两个函数的具体实现。

2.2.1 dequeueBuffer

/frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
        sp<android::Fence> *outFence, uint32_t width, uint32_t height,
        PixelFormat format, uint32_t usage,
        FrameEventHistoryDelta* outTimestamps) {
    
    ……


        int found = BufferItem::INVALID_BUFFER_SLOT;
    while (found == BufferItem::INVALID_BUFFER_SLOT) {
        //1,寻找Free状态的BufferSlot
        status_t status =
            waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,&found);
    }

    //2,把找到的slot放到mActiveBuffers中管理
    if (mCore->mSharedBufferSlot != found) {
        mCore->mActiveBuffers.insert(found);
    }
    *outSlot = found;
    ATRACE_BUFFER_INDEX(found);

    attachedByConsumer = mSlots[found].mNeedsReallocation;
    mSlots[found].mNeedsReallocation = false;

    //修改mBufferState状态为DEQUEUE状态
    mSlots[found].mBufferState.dequeue();
    //3,如果找到的GraphicBuffer是空的,或者需要重新申请
    // 则把设置到BufferSlot的参数全部初始化
    if ((buffer == NULL) ||
        buffer->needsReallocation(width,
            height, format, BQ_LAYER_COUNT, usage))
    {
        mSlots[found].mAcquireCalled = false;
        mSlots[found].mGraphicBuffer = NULL;
        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;

        returnFlags |= BUFFER_NEEDS_REALLOCATION;
    } else {
        // We add 1 because that will be the frame number 
        // when this buffer is queued
        mCore->mBufferAge =
            mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
    }
    ……

   if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
       //4,如果GraphicBuffer为空,则重新创建GraphicBuffer,并放入对应的slot中
        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->mIsAbandoned) {
                //将新创建的GraphicBuffer放入对应的BufferSLot中
                graphicBuffer->setGenerationNumber(
                    mCore->mGenerationNumber);
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
            }
            ……
        } 
    }
    ......
    return returnFlags;
}

dequeueBuffer 函数做的事情主要如下

  • 执行 waitForFreeSlotThenRelock ,查找 BufferQueueCore 的 Slot 中空闲的插槽位置,并且取出 BufferSlot 中的 GraphicBuffer 的 index,判断当前这个图元是否宽高,像素格式是否和请求一样,不一样则需要重新请求
  • 先把 found 的 index 添加到 mActiveBuffer 集合中,标示为活跃状态,并且设置为 dequeue 状态
  • 如果找到的 BufferSlot 的 GraphicBuffer 为空或者需要重新申请,则把设置到 BufferSlot 的参数全部初始化
  • 如果需要重新创建 GraphicBuffer,则创建一个新的 GraphicBuffer,接着调用 GraphicBuffer 的 initCheck 进行校验,并把新的 GraphicBuffer 加入到 mSlot

2.2.2 queueBuffer

了解了 dequeueBuffer 的流程,在接着看 queueBuffer,queuebuffer 是生产者使用完 GraphicBuffer 后把 GraphicBuffer 放回 BufferQueue,并把 BufferState 修改成 QUEUE 状态的流程,我们看一下源码

/frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
  

    ……
        
    BufferItem item;
    { 

        //错误处理
        ……

        mSlots[slot].mFence = acquireFence;
        //1,修改mBufferState状态为QUEUE状态
        mSlots[slot].mBufferState.queue();

        // Increment the frame counter and store a local version of it
        // for use outside the lock on mCore->mMutex.
        ++mCore->mFrameCounter;
        currentFrameNumber = mCore->mFrameCounter;
        mSlots[slot].mFrameNumber = currentFrameNumber;
        //2,BufferItem复制
        item.mAcquireCalled = mSlots[slot].mAcquireCalled;
        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
        item.mCrop = crop;
        item.mTransform = transform &
                ~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
        item.mTransformToDisplayInverse =
                (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
        item.mScalingMode = static_cast<uint32_t>(scalingMode);
        item.mTimestamp = requestedPresentTimestamp;
        item.mIsAutoTimestamp = isAutoTimestamp;
        item.mDataSpace = dataSpace;
        item.mFrameNumber = currentFrameNumber;
        item.mSlot = slot;
        item.mFence = acquireFence;
        item.mFenceTime = acquireFenceTime;
        item.mIsDroppable = mCore->mAsyncMode ||
                mCore->mDequeueBufferCannotBlock ||
                (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
        item.mSurfaceDamage = surfaceDamage;
        item.mQueuedBuffer = true;
        item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;

        mStickyTransform = stickyTransform;       

        output->bufferReplaced = false;
        if (mCore->mQueue.empty()) {
            // BufferQueueCore的BufferSlot队列为空时,直接push到队尾
            mCore->mQueue.push_back(item);
            frameAvailableListener = mCore->mConsumerListener;
        } else {
            // 队列不为空,需要判断一下last BufferItem是否被替换
            // 如果可以替换就替换,如果不可以替换就直接把BufferItem放到mQueue尾部
            const BufferItem& last = mCore->mQueue.itemAt(
                    mCore->mQueue.size() - 1);
            if (last.mIsDroppable) {

                if (!last.mIsStale) {
                    mSlots[last.mSlot].mBufferState.freeQueued();

                    if (!mCore->mSharedBufferMode &&
                            mSlots[last.mSlot].mBufferState.isFree()) {
                        mSlots[last.mSlot].mBufferState.mShared = false;
                    }            
                    if (!mSlots[last.mSlot].mBufferState.isShared()) {
                        mCore->mActiveBuffers.erase(last.mSlot);
                        mCore->mFreeBuffers.push_back(last.mSlot);
                        output->bufferReplaced = true;
                    }
                }

                // Overwrite the droppable buffer with the incoming one
                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                frameReplacedListener = mCore->mConsumerListener;
            } else {
                mCore->mQueue.push_back(item);
                frameAvailableListener = mCore->mConsumerListener;
            }
        }

       ……
    } 

    return NO_ERROR;
}

queueBuffer 的流程主要做了这两件事情:

  • 将对应 BufferSlot 状态设置成 QUEUED
  • 创建 BufferItem 对象,并将 GraphicBuffer 的数据复制给 BufferItem,并入队到 BufferQueueCore 的 mQueue 队列中,这样可以方便图像消费者直接按先进先出的顺序从 mQueue 队列取出 GraphicBuffer 使用

2.3 BufferQueueConsumer

了解了 BufferQueueCore 和 BufferQueueProducer,接着看 BufferQueue 的最后一个元素:BufferQueueConsumer,在概览中讲到过,图像消费者 SurfceFlinger 通过 acquire 来获取图像缓冲区,通过 release 来释放该缓冲区。下面就看看 BufferQueueConsumer 中 acquire 和 release 两个操作的具体流程。

2.3.1 acquireBuffer

先看 acquireBuffer 的过程

/frameworks/native/libs/gui/BufferQueueConsumer.cpp

status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
        nsecs_t expectedPresent, uint64_t maxFrameNumber) {
 
    int numDroppedBuffers = 0;
    sp<IProducerListener> listener;
    {
        //1,检查acquire的buffer的数量是否超出了限制
        ……

        //2.检查BufferQueueCore中的mQueue队列是否为空
        if (mCore->mQueue.empty() && !sharedBufferAvailable) {
            return NO_BUFFER_AVAILABLE;
        }

        //获取BufferQueueCore中的mQueue队列的迭代器
        BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
        int slot = BufferQueueCore::INVALID_BUFFER_SLOT;

        if (sharedBufferAvailable && mCore->mQueue.empty()) {
            //共享Buffer模式
        } else {
            //从front获取对应的slot
            slot = front->mSlot;
            *outBuffer = *front;
        }       

        if (!outBuffer->mIsStale) {
            mSlots[slot].mAcquireCalled = true;           
            if (mCore->mQueue.empty()) {
                mSlots[slot].mBufferState.acquireNotInQueue();
            } else {
                //将BufferState状态改为acquire
                mSlots[slot].mBufferState.acquire();
            }
            mSlots[slot].mFence = Fence::NO_FENCE;
        }

        if (outBuffer->mAcquireCalled) {
            outBuffer->mGraphicBuffer = NULL;
        }
        //将该Buffer从mQueue中移除
        mCore->mQueue.erase(front);        
    }

    //回调
    ……
    return NO_ERROR;
}

acquireBuffer 函数中的逻辑也非常的清晰,主要就是这几件事情

  • 判断 BufferQueueCore 中的 mQueue 是否为空,mQueue 就是前面 BufferQueueProducer 调用 queueBuffer 函数时,将缓冲区入队的容器
  • 取出对应的 BufferSlot
  • 将 BufferState 改为 acquire 状态
  • 将该 Buffer 从 mQueue 中移除

2.3.2 releaseBuffer

再接着看 releaseBuffer 的流程

/frameworks/native/libs/gui/BufferQueueConsumer.cpp

status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
        const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
        EGLSyncKHR eglFence) {
  
    //slot合法性校验
    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
            releaseFence == NULL) {      
        return BAD_VALUE;
    }

    {       
        //slot状态校验
        if (!mSlots[slot].mBufferState.isAcquired()) {         
            return BAD_VALUE;
        }        
        ......
        //将BufferState转为release
        mSlots[slot].mBufferState.release();
        ......
        // 将slot放在FreeBuffers中
        if (!mSlots[slot].mBufferState.isShared()) {
            mCore->mActiveBuffers.erase(slot);
            mCore->mFreeBuffers.push_back(slot);
        }
    } 

    // 回调
    if (listener != NULL) {
        listener->onBufferReleased();
    }
    return NO_ERROR;
}

releaseBuffer 的流程也比较简单,主要是将 BufferState 改为 release 状态,并将该 BufferlSlot 放在FreeBuffers 这个容器中。

三 GraphicBuffer的创建

GraphicBuffer 是图像缓冲区的最后一个组成部分,也是最基本最重要的一个单元,在前面讲到 BufferQueueProucer 通过 dequeueBuffer 获取 BufferSlot 时,会判断 BufferSlot 中的 GraphicBuffer 是否为空,如果为空就会创建新的 GraphicBuffer。

status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
        sp<android::Fence> *outFence, uint32_t width, uint32_t height,
        PixelFormat format, uint32_t usage,
        FrameEventHistoryDelta* outTimestamps) {    
    ......
   if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
       //如果GraphicBuffer为空,则重新创建GraphicBuffer,并放入对应的slot中
        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->mIsAbandoned) {
                //将新创建的GraphicBuffer放入对应的BufferSLot中
                graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
            }
            ......
        } 
    }
    ......
    return returnFlags;
}

可以看到在 dequeueBuffer 会创建 GraphicBuffer,GraphicBuffer 是什么呢?我们先看看它的构造函数

/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));
}

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) {
        width = static_cast<int>(inWidth);
        height = static_cast<int>(inHeight);
        format = inFormat;
        layerCount = inLayerCount;
        usage = static_cast<int>(inUsage);
        stride = static_cast<int>(outStride);
    }
    return err;
}

GraphicBuffer 的构造函数调用 initWithSize 方法创建指定大小的 Buffer,流程如下

  • 获取 GraphicBufferAllocator,它是图像缓冲区的内存分配器
  • 调用 GraphicBufferAllocator.allocate 分配缓冲内存

/frameworks/native/libs/ui/GraphicBufferAllocator.cpp

GraphicBufferAllocator::GraphicBufferAllocator()
  : mMapper(GraphicBufferMapper::getInstance()),
    mAllocator(std::make_unique<Gralloc2::Allocator>(
                mMapper.getGrallocMapper()))
{
}

从 GraphicBufferAllocator 的构造函数中可以看到,真正的缓冲区分配器是 Gralloc2::Allocator 模块。由于篇幅原因,在这里就不在深入讲解 Gralloc 了,我们只需要了解 Gralloc 是位于 HAL(硬件抽象层)中的模块,封装了对帧缓冲区的所有访问创建等操作。

四 图像生产者使用缓冲区

我们已经对图像缓冲区有了一定的了解了,包括 Surface,Layer,BufferQueue 的创建和使用也都熟悉了。那么接下来看看图像消费者在 Android 中使用图像缓冲区的场景:软件绘制、硬件加速。

4.1 软件绘制

在图像生产者中介绍 Skia 时讲到过,软件绘制的入口函数 drawSoftware 的三步操作。

/frameworks/base/core/java/android/view/ViewRootImpl.java

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty) {

    final Canvas canvas;

    canvas = mSurface.lockCanvas(dirty);
    ……
    mView.draw(canvas);
    ……
    surface.unlockCanvasAndPost(canvas);
    ……
    return true;
}
  • 通过 mSurface.lockCanvas 获取 Canvas
  • 通过 draw 方法,将根 View 及其子 View 遍历绘制到 Canvas 上
  • 通过 surface.unlockCanvasAndPost 将绘制内容提交给 surfaceFlinger 进行合成

第一步会创建或获取图像缓冲区,第三步会提交图像缓冲区。

4.1.1 获取图像缓冲区

/frameworks/base/core/java/android/view/Surface.java

public Canvas lockCanvas(Rect inOutDirty)
    throws Surface.OutOfResourcesException, IllegalArgumentException {
    synchronized (mLock) {        
        mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
        return mCanvas;
    }
}

lockCanvas 会调用 native 方法 nativeLockCanvas

/frameworks/base/core/jni/android_view_Surface.cpp

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));

    if (!isSurfaceValid(surface)) {
        doThrowIAE(env);
        return 0;
    }

    Rect dirtyRect(Rect::EMPTY_RECT);
    Rect* dirtyRectPtr = NULL;

    if (dirtyRectObj) {
        dirtyRect.left   = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
        dirtyRect.top    = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
        dirtyRect.right  = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
        dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
        dirtyRectPtr = &dirtyRect;
    }

    ANativeWindow_Buffer outBuffer;
    //1,获取用来存储图形绘制的buffer
    status_t err = surface->lock(&outBuffer, dirtyRectPtr);
    

    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                         convertPixelFormat(outBuffer.format),
                                         outBuffer.format == PIXEL_FORMAT_RGBX_8888
                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType,
                                         GraphicsJNI::defaultColorSpace());

    SkBitmap bitmap;
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    bitmap.setInfo(info, bpr);
   
    if (outBuffer.width > 0 && outBuffer.height > 0) {
         //将上一个buffer里的图形数据复制到当前bitmap中
        bitmap.setPixels(outBuffer.bits);
    } else {
        // be safe with an empty bitmap.
        bitmap.setPixels(NULL);
    }

    //2,创建一个 SkCanvas
    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    //3,给SKCanvas设置Bitmap
    nativeCanvas->setBitmap(bitmap);
    //如果指定了脏区,则设定脏区的区域
    if (dirtyRectPtr) {
        nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
    }

    if (dirtyRectObj) {
        env->SetIntField(dirtyRectObj, gRectClassInfo.left,   dirtyRect.left);
        env->SetIntField(dirtyRectObj, gRectClassInfo.top,    dirtyRect.top);
        env->SetIntField(dirtyRectObj, gRectClassInfo.right,  dirtyRect.right);
        env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
    }

    sp<Surface> lockedSurface(surface);
    lockedSurface->incStrong(&sRefBaseOwner);
    return (jlong) lockedSurface.get();
}

nativeLockCanvas 函数中会调用了 surface->lock 函数来获取一个供 SkCanvas 使用的缓冲区,看一下具体实现。

/frameworks/native/libs/gui/Surface.cpp

status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{

    ......
    ANativeWindowBuffer* out;
    int fenceFd = -1;
    //1.调用dequeueBuffer获取图像缓冲区
    status_t err = dequeueBuffer(&out, &fenceFd);
   
    if (err == NO_ERROR) {
        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
        const Rect bounds(backBuffer->width, backBuffer->height);

        Region newDirtyRegion;
        if (inOutDirtyBounds) {
            newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
            newDirtyRegion.andSelf(bounds);
        } else {
            newDirtyRegion.set(bounds);
        }

        
        const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
        const bool canCopyBack = (frontBuffer != 0 &&
                backBuffer->width  == frontBuffer->width &&
                backBuffer->height == frontBuffer->height &&
                backBuffer->format == frontBuffer->format);

        if (canCopyBack) {
            // 2,如果前一帧的缓冲区大小和格式和当前一样,则将前一帧的缓冲区数据拷贝
            // 到当前获取的缓冲区来,这样能复用数据,节约性能。
            const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
            if (!copyback.isEmpty()) {
                copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
            }
        } else {
           ……
        }


       ……

        void* vaddr;
        status_t res = backBuffer->lockAsync(
                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                newDirtyRegion.bounds(), &vaddr, fenceFd);

        ……
    }
    return err;
}

我们直接看 dequeueBuffer 是如何获取缓冲区的

/frameworks/native/libs/gui/Surface.cpp

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
   
   ……
    //创建缓冲区
   status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
            reqWidth, reqHeight, reqFormat, reqUsage,
            enableFrameTimestamps ? &frameTimestamps : nullptr);
   

    if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
        //映射缓冲区
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
        
    }

    return OK;
}

dequeueBuffer 关键的步骤只有两步

  • 通过 GraphicBufferProducer 的 dequeueBuffer 获取缓冲区
  • 通过 GraphicBufferProducer 的 requestBuffer 映射缓冲区

dequeueBuffer 已经讲过了,就不重复讲了,我们看一下 requestBuffer,当我们获取了缓冲区内存后,图像生产者和图像消费者都需要将缓冲区映射到他们的进程中才能使用。

/frameworks/native/libs/gui/IGraphicBufferProducer.cpp

virtual 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) {
        //在用户进程创建空的GraphicBuffer
        *buf = new GraphicBuffer();
        //通过共享内存的方式,映射GraphicBufferProducer中的GraphicBuffer
        result = reply.read(**buf);
        if(result != NO_ERROR) {
            (*buf).clear();
            return result;
        }
    }
    result = reply.readInt32();
    return result;
}

可以看到,requestBuffer 函数中创建了 GraphicBuffer 对象,这个对应位于用户进程,并且没有申请内存。接着调用了 Parcel 的 read 的方法,将 GraphicBufferProducer 中的 GraphicBuffer 映射过来。共享内存是最高效也是传输数据量最大的 IPC 通信机制。

4.1.2 提交图像缓冲区

接着看软绘是如何提交缓冲区,它的实现在 nativeUnlockCanvasAndPost 这个 native 函数中

/frameworks/base/core/jni/android_view_Surface.cpp

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj) {
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    if (!isSurfaceValid(surface)) {
        return;
    }

    // detach the canvas from the surface
    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    nativeCanvas->setBitmap(SkBitmap());

    // unlock surface
    status_t err = surface->unlockAndPost();
    if (err < 0) {
        doThrowIAE(env);
    }
}

nativeUnlockCanvasAndPost 调用了 surface 的 unlockAndPost 函数

/frameworks/native/libs/gui/Surface.cpp

status_t Surface::unlockAndPost()
{
    if (mLockedBuffer == 0) {
        return INVALID_OPERATION;
    }

    int fd = -1;
    status_t err = mLockedBuffer->unlockAsync(&fd);

    //图像缓冲区的入队操作
    err = queueBuffer(mLockedBuffer.get(), fd);
   

    mPostedBuffer = mLockedBuffer;
    mLockedBuffer = 0;
    return err;
}

在着看 queueBuffer 的实现

/frameworks/native/libs/gui/Surface.cpp

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
   
    Mutex::Autolock lock(mMutex);
    int64_t timestamp;
    bool isAutoTimestamp = false;

    //1,获取Buffer在slots中的index
    int i = getSlotFromBufferLocked(buffer);
    if (i < 0) {
        if (fenceFd >= 0) {
            close(fenceFd);
        }
        return i;
    }
    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
        if (fenceFd >= 0) {
            close(fenceFd);
        }
        return OK;
    }
    ......
    //2,调用GraphicBufferProducer的queueBuffer函数,进行入队操作
    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
    mLastQueueDuration = systemTime() - now;
    if (err != OK)  {
        ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
    }

    ……

    return err;
}

可以看到 surface.unlockCanvasAndPost 最终也是通过 queueBuffer 函数来提交缓冲区的。

4.2 硬件加速绘制

了解了软件绘制如何使用缓冲区,我们在看看硬件加速中是如何使用缓冲区的,硬件加速的绘制入口在 CanvasContext 的 draw 函数中,对硬件绘制流程不熟的,可以看图像生产者中使用 OpenGL 进行硬件绘制的部分。

/frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

void CanvasContext::draw() {
    SkRect dirty;
    mDamageAccumulator.finish(&dirty);

    mCurrentFrameInfo->markIssueDrawCommandsStart();
    //1,获取缓冲区
    Frame frame = mRenderPipeline->getFrame();

    SkRect windowDirty = computeDirtyRect(frame, &dirty);
    //2,调用OpenGL的draw函数进行绘制
    bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
            mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler()));

    waitOnFences();

    bool requireSwap = false;
    //3,提交缓冲区
    bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo,
            &requireSwap);

    mIsDirty = false;
    ......
}

硬件加速和软件绘制的流程都是分为三步

  • 获取缓冲区
  • 绘制
  • 提交缓冲区

4.2.1 获取图像缓冲区

我们先看第一步:获取缓冲区。它的是在 RenderPipeline 的 getFrame 函数中,这里的 RenderPipeline 以 OpenGLPipeline 为例讲解。

/frameworks/base/libs/hwui/renderthread/OpenGLPipeline.cpp

Frame OpenGLPipeline::getFrame() {
    return mEglManager.beginFrame(mEglSurface);
}

getFrame 调用了 EglManager 的 beginFrame 函数

Frame EglManager::beginFrame(EGLSurface surface) {    
    makeCurrent(surface);
    Frame frame;
    frame.mSurface = surface;
    eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
    eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
    frame.mBufferAge = queryBufferAge(surface);
    eglBeginFrame(mEglDisplay, surface);
    return frame;
}

beginFrame 中的流程有这几步

  • 调用 makeCurrent 获取缓冲区
  • 调用 eglQuerySurface 获取缓冲区的宽高
  • 调用 eglBeginFrame 进行参数合法性校验

关键函数是 makeCurrent,直接看它的实现

/frameworks/base/libs/hwui/renderthread/EglManager.cpp

bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
    
    if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
        if (errOut) {
            *errOut = eglGetError();          
        } else {
           
        }
    }
    mCurrentSurface = surface;
    if (Properties::disableVsync) {
        eglSwapInterval(mEglDisplay, 0);
    }
    return true;
}

makeCurrent 实际调用的是 eglMakeCurrent,这部分的实现在 EGL 里面

/frameworks/native/opengl/libs/EGL/egl.cpp

EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
                            EGLSurface read, EGLContext ctx)
{
    
    //draw和read和合法校验
    ……

    if (ctx == EGL_NO_CONTEXT) {      
        current_ctx = (EGLContext)getGlThreadSpecific();
    } else {
        egl_context_t* c = egl_context_t::context(ctx);
        //将EGLSurface转换成egl_surface_t
        egl_surface_t* d = (egl_surface_t*)draw;
        egl_surface_t* r = (egl_surface_t*)read;

    }

    ogles_context_t* gl = (ogles_context_t*)ctx;
    if (makeCurrent(gl) == 0) {
        if (ctx) {
            
            egl_context_t* c = egl_context_t::context(ctx);
            egl_surface_t* d = (egl_surface_t*)draw;
            egl_surface_t* r = (egl_surface_t*)read;
            
            if (c->draw) {
                egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw);
                s->disconnect();
                s->ctx = EGL_NO_CONTEXT;
                if (s->zombie)
                    delete s;
            }
            if (c->read) {
                
            }
            
            c->draw = draw;
            c->read = read;

            if (c->flags & egl_context_t::NEVER_CURRENT) {
                c->flags &= ~egl_context_t::NEVER_CURRENT;
                GLint w = 0;
                GLint h = 0;
                if (draw) {
                    w = d->getWidth();
                    h = d->getHeight();
                }
                ogles_surfaceport(gl, 0, 0);
                ogles_viewport(gl, 0, 0, w, h);
                ogles_scissor(gl, 0, 0, w, h);
            }
            if (d) {
                //1,调用egl_surface_t的connect函数创建缓冲区
                if (d->connect() == EGL_FALSE) {
                    return EGL_FALSE;
                }
                d->ctx = ctx;                
                d->bindDrawSurface(gl);
            }
            if (r) {               
                r->ctx = ctx;
                r->bindReadSurface(gl);
            }
        } else {
            //异常处理
            ……
        }
        return EGL_TRUE;
    }
    return setError(EGL_BAD_ACCESS, EGL_FALSE);
}

eglMakeCurrent 函数中我们只关心一件事情:执行 d->connect() 创建缓冲区

/frameworks/native/opengl/libs/EGL/egl.cpp

EGLBoolean egl_window_surface_v2_t::connect() 
{
    //获取图像缓冲区
    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer,
            &fenceFd) != NO_ERROR) {
        return setError(EGL_BAD_ALLOC, EGL_FALSE);
    }
    ……
    // 绑定缓冲区
    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | 
            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
        return setError(EGL_BAD_ACCESS, EGL_FALSE);
    }
    return EGL_TRUE;
}

可以看到 connect 函数中最终依然是调用了nativeWindow 的 dequeueBuffer 函数,前面提到过 Surface 继承自 nativeWindow,所以这里实际就是调用了 Surface 的 dequeueBuffer 函数,这个函数的实现在前面详细讲了,这儿就不再讲了。但是这里和软件绘制有点区别的地方在于缓冲区的绑定,软件绘制中是调用的 requestBuffer 进行共享内存的绑定,这里是调用 lock 函数,它的实现如下

/frameworks/native/opengl/libs/EGL/egl.cpp

status_t egl_window_surface_v2_t::lock(
        ANativeWindowBuffer* buf, int usage, void** vaddr)
{
    auto& mapper = GraphicBufferMapper::get();
    return mapper.lock(buf->handle, usage,
            android::Rect(buf->width, buf->height), vaddr);
}

lock 函数里面其实调用 GraphicBufferMapper 进行了一个内存绑定的操作,GraphicBufferMapper 属于 Galloc 模块,它是单例的,在 requesetBuffer 函数中进行内存共享绑定操作时,也会用到这个 GraphicBufferMapper,他们是同一个 GraphicBufferMapper。

4.2.2 提交图像缓冲区

我们接着看硬件加速是如何提交缓冲区的,它的实现在 swapBuffers 函数中。

/frameworks/native/opengl/libagl/egl.cpp

EGLBoolean egl_window_surface_v2_t::swapBuffers()
{
    ……
    
    unlock(buffer);
    previousBuffer = buffer;
    //1,提交缓冲区
    nativeWindow->queueBuffer(nativeWindow, buffer, -1);
    buffer = 0;

    int fenceFd = -1;
    //2,获取新的缓冲区
    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR) {
        ……
    } else {
        return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
    }

    return EGL_TRUE;
}

swapBuffers 中的逻辑很清晰,主要就两步操作:

  • queueBuffer 提交缓冲区
  • dequeueBuffer 获取新的缓冲区

关于缓冲区的使用实例这里就讲完了,可以看到,不管是软件绘制,还是硬件加速,使用的方式都是一致的,都为下面几步:

  • dequeueBuffer 获取缓冲区
  • 进行缓冲区内存绑定操作
  • 使用缓冲区存储图像
  • queueBuffer 提交缓冲区

这些步骤是通用的流程,不仅适用在软件绘制或者硬件绘制,还包括 webview,或者是 Flutter,或者我们自己开发渲染引擎,都需要使用同样的步骤。

下面接着看图像消费使用缓冲区的实例。

五 图像消费者使用缓冲区

Android图形显示系统2 图像消费者 这一篇中讲到,SurfaceFlinger 会在 onMessageReceived 收到 VSync 的通知

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::onMessageReceived(int32_t what) {
    ATRACE_CALL();
    switch (what) {
        case MessageQueue::INVALIDATE: {
            bool frameMissed = !mHadClientComposition &&
                    mPreviousPresentFence != Fence::NO_FENCE &&
                    (mPreviousPresentFence->getSignalTime() ==
                            Fence::SIGNAL_TIME_PENDING);
            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
            if (mPropagateBackpressure && frameMissed) {
                ALOGD("Backpressure trigger, skipping transaction & refresh!");
                //如果掉帧则请求下一次VSync,跳过这一次请求
                signalLayerUpdate();
                break;
            }

            //更新VR模式的Flinger
            updateVrFlinger();

            bool refreshNeeded = handleMessageTransaction();
            //获取缓冲区
            refreshNeeded |= handleMessageInvalidate();
            refreshNeeded |= mRepaintEverything;
            if (refreshNeeded) {
                //判断是否要做刷新
                signalRefresh();
            }
            break;
        }
        case MessageQueue::REFRESH: {
            handleMessageRefresh();
            break;
        }
    }
}

INVALIDATE 的流程如下:

  • 判断是否掉帧
  • 通过 handleMessageTransaction 函数来判断是否要处理这一次的 Vsync
  • 调用 handleMessageInvalidate 函数获取 Layer 中的缓冲区
  • 执行 signalRefresh 函数

这些流程在图像消费者都有详细讲过,我们直接看 handleMessageInvalidate 函数的实现

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

bool SurfaceFlinger::handleMessageInvalidate() {
    return handlePageFlip();
}


bool SurfaceFlinger::handlePageFlip()
{

    bool visibleRegions = false;
    bool frameQueued = false;
    bool newDataLatched = false;

   ……

    for (auto& layer : mLayersWithQueuedFrames) {
        //遍历所有Layer,获取缓冲区
        const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
        layer->useSurfaceDamage();
        invalidateLayerStack(layer->getLayerStack(), dirty);
        if (!dirty.isEmpty()) {
            newDataLatched = true;
        }
    }

    mVisibleRegionsDirty |= visibleRegions;

    if (frameQueued && mLayersWithQueuedFrames.empty()) {
        signalLayerUpdate();
    }

    return !mLayersWithQueuedFrames.empty() && newDataLatched;
}

handlePageFlip 函数中调用 layer->latchBuffer() 函数来获取缓冲区

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

Region Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime)
{
  
  
    status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
            mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
            mLastFrameNumberReceived);
   
    ……
        
    return outDirtyRegion;
}

latchBuffer 函数非常的长,流程也很多,但我们只关心它获取图像缓冲区的部分:调用 SurfaceFlingerConsumer 的 updateTexImage 函数。SurfaceFlingerConsumer 是对 BufferQueueConsumer 的封装,我们看看 updateTexImage 的实现。

/frameworks/native/services/surfaceflinger/SurfaceFlingerConsumer.cpp

status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
        const DispSync& dispSync, bool* autoRefresh, bool* queuedBuffer,
        uint64_t maxFrameNumber)
{
    

    BufferItem item;

    //获取缓冲区
    err = acquireBufferLocked(&item, computeExpectedPresent(dispSync),
            maxFrameNumber);
   ……

    // 释放前一个缓冲区
#ifdef USE_HWC2
    err = updateAndReleaseLocked(item, &mPendingRelease);
#else
    err = updateAndReleaseLocked(item);
#endif
    if (err != NO_ERROR) {
        return err;
    }

    ……

    return err;
}

updateTexImage 函数中主要做了两件事情:

  • 调用 acquireBufferLocked 函数获取缓冲区
  • 调用 releaseBufferLocked 释放上一个缓冲区

下面分别看一下这两件事情的实现。

acquireBufferLocked 函数的实现如下:

/frameworks/native/services/surfaceflinger/SurfaceFlingerConsumer.cpp

status_t SurfaceFlingerConsumer::acquireBufferLocked(BufferItem* item,
        nsecs_t presentWhen, uint64_t maxFrameNumber) {
    status_t result = GLConsumer::acquireBufferLocked(item, presentWhen,
            maxFrameNumber);
    if (result == NO_ERROR) {
        mTransformToDisplayInverse = item->mTransformToDisplayInverse;
        mSurfaceDamage = item->mSurfaceDamage;
    }
    return result;
}

这里调用了 GLConsumer 的 acquireBufferLocked,SurfaceFlingerConsumer 是继承自 GLConsumer,所以其实是调用父类方法。

/frameworks/native/libs/gui/ConsumerBase.cpp

status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
        nsecs_t presentWhen, uint64_t maxFrameNumber) {
    if (mAbandoned) {
        CB_LOGE("acquireBufferLocked: ConsumerBase is abandoned!");
        return NO_INIT;
    }
    //获取缓冲区
    status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
    if (err != NO_ERROR) {
        return err;
    }

    
    if (item->mGraphicBuffer != NULL) {
        if (mSlots[item->mSlot].mGraphicBuffer != NULL) {
            freeBufferLocked(item->mSlot);
        }
        mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
    }

    mSlots[item->mSlot].mFrameNumber = item->mFrameNumber;
    mSlots[item->mSlot].mFence = item->mFence;


    return OK;
}

releaseBufferLocked 函数的实现如下

/frameworks/native/libs/gui/ConsumerBase.cpp

status_t ConsumerBase::releaseBufferLocked(
        int slot, const sp<GraphicBuffer> graphicBuffer,
        EGLDisplay display, EGLSyncKHR eglFence) {
    if (mAbandoned) {
        CB_LOGE("releaseBufferLocked: ConsumerBase is abandoned!");
        return NO_INIT;
    }
    //释放缓冲区
    status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber,
            display, eglFence, mSlots[slot].mFence);
    if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) {
        freeBufferLocked(slot);
    }

    mPrevFinalReleaseFence = mSlots[slot].mFence;
    mSlots[slot].mFence = Fence::NO_FENCE;

    return err;
}

可以看到 acquireBufferLocked 和 releaseBufferLocked 最终调用的依然是 BufferQueueConsumer 的 acuqireBuffer 和 releaseBuffer 函数。

六 总结

到这里,图像显示原理的知识就都讲完了。技术飞速发展,Android 中不断有新的图像显示系统出现,如 RN,小程序,以及现在很火的Flutter 等等,当面对层出不穷的新技术时,我们只有掌握底层的本质原理和组成,才能快速的理解新的技术。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android图形显示系统6 图像缓冲区(下) 的相关文章

  • rk3399 高可靠OTA升级

    https blog csdn net m0 37631324 article details 106254910
  • Android 12 源码下载、编译与烧录到Pixel 3a

    android 12 源码下载 编译与烧录到Pixel 3a 当前设备环境 源码下载 温馨跳转 个人总结源码下载 谷歌手机设备驱动的下载 编译 烧录 当前设备环境 操作系统 ubuntu 18 04 LTS 手机 谷歌手机Pixel 3a
  • Android SurfaceFlinger分析

    SufaceFlinger的构成并不是太复杂 复杂的是他的客户端建构 SufaceFlinger主要功能是 1 将Layers Surfaces 内容的刷新到屏幕上 2 维持Layer的Zorder序列 并对Layer 最终输出做出裁剪计算
  • Android显示系统 SurfaceFlinger内部机制 3 APP申请创建Surface的过程

    韦东山 笔记 3 APP申请创建Surface的过程 看看Surface test的过程 1 获取SF服务 2 创建Surface 3 得到buffer 4 写buffer 5 回顾下获取SF服务过程 AP获取SF服务 调用createCo
  • android的消息处理机制(图+源码分析)——Looper,Handler,Message

    android的消息处理有三个核心类 Looper Handler和Message 其实还有一个Message Queue 消息队列 但是MQ被封装到Looper里面了 我们不会直接与MQ打交道 因此我没将其作为核心类 下面一一介绍 线程的
  • Android四种Activity的加载模式

    建议首先阅读下面两篇文章 这样才可以更好的理解Activity的加载模式 Android的进程 线程模型 http www cnblogs com ghj1976 archive 2011 04 28 2031586 html 其中对 An
  • Android混合开发全解析

    现在的app都开始流行混合开发了 这是一个app开发的新技术 作为android程序猿的我们也应该要了解并且掌握他 那么在使用之前 我们一定要搞清楚 我们的哪些场景使用混合开发好一些呢 这个问题一定要搞清楚 因为现在的混合开发还不成熟 We
  • Android 13 - binder阅读(5)- 使用ServiceManager注册服务2

    上一篇笔记我们看到了binder transaction 这个方法很长 这一篇我们将把这个方法拆分开来看binder transaction做了什么 从而学习binder是如何跨进程通信的 1 binder transaction stat
  • Android图形显示系统6 图像缓冲区(下)

    一 概述 我们再次回顾下上一篇文章 Android图形显示系统5 图像缓冲区 上 描述的图像缓冲区 Android 图形缓冲区由哪些部分组成 Android 的图形缓冲区由 Surface BufferQueue Layer Graphic
  • 如何dump SKP,SKP抓取

    1 如何dump SKP 我们知道绘制的操作 主要都是在SkiaPipline renderframe中进行的 frameworks base libs hwui pipeline skia SkiaPipeline cpp 429 voi
  • FBE中的Home界面FallbackHome

    FallbackHome FallbackHome继承Activity 是安卓系统启动后未解锁时的home界面 Settings程序AndroidManifest xml中定义了FallbackHome的intent filter 可以看到
  • Android Watchdog分析

    初始化 Watchdog作为一个独立的线程在SystemServer进程中被初始化 private void startBootstrapServices NonNull TimingsTraceAndSlog t Start the wa
  • UserHandle.ALL、UserHandle.CURRENT、UserHandle.CURRENT_OR_SELF、UserHandle.OWNER的定义与区别

    UserHandle ALL UserHandle CURRENT UserHandle CURRENT OR SELF UserHandle OWNER的定义与区别 UserHandle ALL 设备上所有用户均可接收到广播 UserHa
  • Binder (一) mmap与一次拷贝原理

    Binder机制 跨进程通信IPC 远程过程调用手段RPC 4个角色进行粘合 Client Server Service Manager和Binder驱动程序 整个过程只需要一次拷贝 Binder Driver misc设备 dev bin
  • Android进阶知识树——应用进程的启动过程

    程序的启动是从进程启动开始的 换句话说只有程序进程启动后 程序才会加载和执行 在AMS启动程序时首先会判断当前进程是否启动 对未启动的进程会发送请求 Zygote在收到请求后创建新的进程 1 Zygote监听客户端请求 由Android进阶
  • Android servicemanager进程启动过程

    在分析ServiceManager实例化注册流程前 先放张ServiceManager在Binder体系中的UML图 一 ServiceManager启动流程 查看system core rootdir init rc脚本可知 init进程
  • linkToDeath机制了解和使用

    转自 http www jianshu com p e38f08e34686 在学习Binder和AIDL的过程中遇到的一些有意思的事情 linkToDeath机制 我们先看看官网如何介绍 When working with remote
  • Android 13 - binder阅读(6)- 使用ServiceManager获取服务

    最近事情好多 使用ServiceManager获取服务就暂时先不学习了 不过从之前的学习中我们也大致可以猜到使用ServiceManager获取服务的过程 根据服务名称获取到ServiceManager中服务代理对应的Handle 再到Bi
  • 使用 source insight 代码跳转时出现 symbol not found 问题

    P S 本篇博客是根据自己的经验来写的 如果大家有不同意见随时交流 1 使用 source insight 代码跳转功能时出现 symbol not found 问题一般是有三种可能 在你添加代码时没有选择 Add tree 选项 创建 p
  • 一些关于dagger2的理解(一)

    转自 http blog csdn net shareye1992 article details 51398554 首先 真实的原理我不准 但是我还是提供我的理解 阅读这篇文章希望读者能满足一个假设 不管懂不懂 看过一些其他关于dagge

随机推荐

  • pytorch 下载

    pytorch 下载 使用anconda 直接下载pytorch的朋友应该是知道pytorch是个啥的 所以直接上教程 anconda下载 anconda是一个用于下载和管理python依赖包和环境的一个工具 下载详情可以去看这篇文章 an
  • VMware虚拟机nat模式连不上网

    我的虚拟机总是各种连不上网 每次都要折腾一番 现在我把虚拟机连不上网的原因总体排查一下 按照流程一步步来 基本上可以解决大部分人的问题 首先 在VMware的编辑 gt 虚拟网络编辑器重新建立 网络 之前的要删掉 新建的同样选择 就可以 如
  • 线性回归总结

    向量相似理论 线性回归 比如预测房价中学区属性0 4 居住体验0 2 通勤距离0 2 商业环境0 2等因素 在同一价格区间 只有样本特征与上述属性分布一致时 各方面都加权均衡 才能取得高分 任一单一属性过高 必然导致其他属性降低 通常意义上
  • Flutter音频播放之just_audio

    just audio的使用 just audio 它是一个用于播放音频的 Flutter 插件 安装和导入 just audio 要使用 just audio 库 需要将其添加到项目的 pubspec yaml 文件中 dependenci
  • Update your application to remove the dependency cycle between beans

    Spring 高版本循环依赖问题 问题描述 提示 Spring boot 应用启动报错 Relying upon circular references is discouraged and they are prohibited by d
  • 三大战略引擎加速转动,微盟驶入智慧商业服务深水区

    2023年3月30日 微盟披露了2022年财报 经调整总收入18 39亿元 经调整毛利11 2亿元 在业务层面 订阅解决方案业务表现亮眼 其中智慧零售板块营收5 13亿元 同比内生增长45 5 拉动每用户平均收益同比增长12 3 达1296
  • (四) 区块链数据结构 – 脚本

    脚本是交易数据中的核心部分 可用于锁定输出和解锁输入 当向某人支付比特币时 我们要为交易输入设置解锁脚本 向别人证明我们有全力使用该输入 同时我们还需要对交易输出添加锁定脚本 确保只有接收者能解锁该输出 脚本 比特币系统专门设计了一套脚本语
  • games101——作业1

    文章目录 作业要求 代码框架 已有代码解读 作业部分代码 进阶部分代码 编译 结果 作业要求 在接下来的三次作业中 我们将要求你去模拟一个基于 CPU 的光栅化渲染器的简化版本 这次作业简要来说就是补全两个函数的内容 一个是 get mod
  • 数据结构实验9:并查集的使用

    问题描述 给定一个图 图中有N个顶点 1 lt N lt 500 编号依次为1 2 3 N 部分顶点之间存在一条无向边 请找出图中所有的极大连通子图 其中 极大联通子图可以描述为该子图中任意两个顶点之间都存在一条路径 且加入任何一个不在该子
  • 会议论文_干货

    研鹿论文 沿路有我 写好论文就找我 有很多同学对会议论文和期刊论文的界定并不是那么明确 那么小鹿今天就为大家详细介绍一下吧 1 会议论文是针对某个学术会议投稿的 且由学术会议的会务组决定是否录用 期刊论文则是针对某学术期刊投稿的 且是由期刊
  • python并发编程:协程asyncio、多线程threading、多进程multiprocessing

    python并发编程 协程 多线程 多进程 CPU密集型计算与IO密集型计算 多线程 多进程与协程的对比 多线程 创建多线程的方法 多线程实现的生产者 消费者爬虫 Lock解决线程安全问题 使用线程池ThreadPoolExecutor 多
  • 深度学习的局部响应归一化LRN(Local Response Normalization)理解

    1 其中LRN就是局部响应归一化 这个技术主要是深度学习训练时的一种提高准确度的技术方法 其中caffe tensorflow等里面是很常见的方法 其跟激活函数是有区别的 LRN一般是在激活 池化后进行的一中处理方法 AlexNet将LeN
  • github 配置了公钥依旧提示git@github.com‘s password: Permission denied, please try again. 的解决办法

    最近在给新电脑配置GitHub的ssh时 一切都是按照流程进行github上文档的配置流程进行配置 但是把公钥配置到github后 在对仓库进行操作的时候依旧出现一下提示 git github com s password Permissi
  • c++链表实现多项式相加

    文章目录 链表实现多项式相加 数据结构 结构体定义 链表 多项式 初始化 Insert 插入单个节点 多项式的某一项 input 输入 sum 求和函数 print 输出函数 测试代码 测试结果 链表实现多项式相加 例如 已知多项式 L 1
  • 良心分享:基于Java+SpringBoot+Netty+WebSocket+Uniapp轻松搭建准实时聊天问答程序

    一步一步教你搭建准实时聊天问答程序 微信小程序 H5网页 本文将详细介绍如何基于你自己的开源项目搭建一个准实时聊天问答程序 包括微信小程序和H5网页版 该项目服务端主要使用了Java Spring Boot Netty WebSocket等
  • 傻瓜式鸿蒙3.0使用Google(无需电脑)

    首先声明 此文仅做交流学术及为出国用户提供微不足道的帮助用 请遵守我国相关法律法规 此文仅做交流学术及为出国用户提供微不足道的帮助用 请遵守我国相关法律法规 此文仅做交流学术及为出国用户提供微不足道的帮助用 请遵守我国相关法律法规 可以先给
  • jsp+mysql分页技巧:巧用limit 进行分页查询

    发现问题 今天检查web程序 浏览 彩信xxxx日志 时 突然发现该web程序中不能浏览了 出错了 如下 500 Servlet Exception java lang OutOfMemoryError Resin 3 0 6 built
  • 文件描述符的阻塞与非阻塞设置

    默认文件描述符是阻塞的 即文件IO是阻塞的 设置为非阻塞 int setNonBlock int fd int flags fcntl fd F GETFL if flags 1 return flags flags O NONBLOCK
  • Qt递归获取指定文件夹下的所有文件

    方法一 使用类QDirIterator来进行遍历 简介 大概是说 适合于大目录遍历 支持递归但是不支持排序 QDirIterator NoIteratorFlags默认值 没有标志 迭代器将返回path符合QDir Filters的条目 Q
  • Android图形显示系统6 图像缓冲区(下)

    一 概述 我们再次回顾下上一篇文章 Android图形显示系统5 图像缓冲区 上 描述的图像缓冲区 Android 图形缓冲区由哪些部分组成 Android 的图形缓冲区由 Surface BufferQueue Layer Graphic