AndroidQ 圖形系統(3)dequeueBuffer函數分析

上一篇文章分析了生產者-消費者模型,構成此模型最重要的三個類就是生產者BufferQueueProducer,消費者BufferQueueConsumer,buffer隊列BufferQueue,而buffer隊列的核心就是BufferQueueCore

本篇文章來分析一下dequeueBuffer這個函數,這個函數的作用是應用程序一端請求繪製圖像時,向BufferQueue中申請一塊可用的GraphicBuffer,有了這個buffer就可以繪製圖像數據了。

在分析這個函數之前先來看看BufferQueueCore中維護的許多生產者-消費者模型需要用到的基本數據結構,首先來看看成員變量Fifo,如下所示:

typedef Vector<BufferItem> Fifo;

這是一個BufferItemVectorBufferItem是用來封裝生產者生產的具體圖形信息的,每次queueBuffer時都會創建一個,我們說的BufferQueue隊列其實就是這個數據結構,使用如下成員變量mQueue來表示的

// mQueue is a FIFO of queued buffers used in synchronous mode.
    Fifo mQueue;

再來看看成員變量mSlots

    BufferQueueDefs::SlotsType mSlots;

BufferQueueDefs::SlotsTyp定義在BufferQueueDefs.h中:

namespace BufferQueueDefs {
        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
    }

其實mSlots就是一個BufferSlot數組,大小爲NUM_BUFFER_SLOTS等於64,BufferSlot主要是用來綁定GraphicBuffer的,一個BufferSlot綁定一個GraphicBuffer,它很重要,它裏面還有一個BufferState專門用來描述當前存儲的這個GraphicBuffer的狀態:

BufferState()
    : mDequeueCount(0),
      mQueueCount(0),
      mAcquireCount(0),
      mShared(false) {
    }
    //         | mShared | mDequeueCount | mQueueCount | mAcquireCount |
    // --------|---------|---------------|-------------|---------------|
    // FREE    |  false  |       0       |      0      |       0       |
    // DEQUEUED|  false  |       1       |      0      |       0       |
    // QUEUED  |  false  |       0       |      1      |       0       |
    // ACQUIRED|  false  |       0       |      0      |       1       |
    // SHARED  |  true   |      any      |     any     |      any      |
    //

BufferState的5種狀態:FREEDEQUEUEDQUEUEDACQUIRED SHARED,狀態由引用計數來表示的,例如mDequeueCount爲1,mSharedfalsemQueueCountmAcquireCount都爲0則表示狀態爲DEQUEUED

再看看成員變量mFreeSlots

// mFreeSlots contains all of the slots which are FREE and do not currently
    // have a buffer attached.
    std::set<int> mFreeSlots;

mFreeSlots代表所有沒有綁定GraphicBufferBufferSlot集合,並且BufferSlot狀態爲FREE

繼續看成員變量mFreeBuffers

 // mFreeBuffers contains all of the slots which are FREE and currently have
    // a buffer attached.
    std::list<int> mFreeBuffers;

mFreeBuffers代表所有綁定了GraphicBufferBufferSlot集合,並且BufferSlot狀態爲FREE

繼續看成員變量mActiveBuffers

// mActiveBuffers contains all slots which have a non-FREE buffer attached.
    std::set<int> mActiveBuffers;

mActiveBuffers代表所有綁定了GraphicBufferBufferSlot集合,並且BufferSlot狀態不爲FREE,可以是另外四種狀態的任意一種。

繼續看成員變量mUnusedSlots

 // mUnusedSlots contains all slots that are currently unused. They should be
    // free and not have a buffer attached.
    std::list<int> mUnusedSlots;

mUnusedSlots代表當前沒有使用的BufferSlot集合。
需要注意是以上的數據結構存儲的都是BufferSlot的index

這幾個數據結構都是爲了給BufferSlot分類,以便獲取GraphicBuffer時更加高效。

接着我們分析申請GraphicBuffer的函數dequeueBuffer,從Surface.cppdequeueBuffer爲入口開始,這個函數比較多,分部分來看看

dequeueBuffer

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();
    ALOGV("Surface::dequeueBuffer");

    uint32_t reqWidth;
    uint32_t reqHeight;
    PixelFormat reqFormat;
    uint64_t reqUsage;
    bool enableFrameTimestamps;

    {
        Mutex::Autolock lock(mMutex);
        if (mReportRemovedBuffers) {
            mRemovedBuffers.clear();
        }

        reqWidth = mReqWidth ? mReqWidth : mUserWidth;
        reqHeight = mReqHeight ? mReqHeight : mUserHeight;

        reqFormat = mReqFormat;
        reqUsage = mReqUsage;

        enableFrameTimestamps = mEnableFrameTimestamps;

        if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                BufferItem::INVALID_BUFFER_SLOT) {
            sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
            if (gbuf != nullptr) {
                *buffer = gbuf.get();
                *fenceFd = -1;
                return OK;
            }
        }
    } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer  
    	......
 }

第一部分主要是給一些buffer的參數設置,這些值由外部應用程序設置,如buffer寬高,格式等,接着如果滿足mSharedBufferMode等一些條件則通過index爲mSharedBufferSlot直接從mSlots中獲取buffer,不太清楚mSharedBufferMode這是一種什麼模式,默認值爲false,想必是外部應用程序請求的,暫時不管了

接着看第二部分,我們重點需要分析的dequeueBuffer函數調用:

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
	......
	int buf = -1;
    sp<Fence> fence;
    nsecs_t startTime = systemTime();

    FrameEventHistoryDelta frameTimestamps;
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
                                                            reqFormat, reqUsage, &mBufferAge,
                                                            enableFrameTimestamps ? &frameTimestamps
                                                                                  : nullptr);
	......

Fence和資源同步相關,其實可以看到dequeueBuffer的很多參數都是生產者這邊傳遞過去對buffer的一些設置,重要的是buf這個參數,它是個int類型,代表dequeueBuffer獲取到的BufferSlot的下標,前面我們分析過,BufferQueueCore中有幾個用來管理BufferSlot的數據結構mFreeSlotsmFreeBuffers等保存的都是下標,這裏將buf傳遞過去,最終獲取到可用BufferSlot的下標,再根據下標從mSlots中獲取BufferSlot,這點等下再說,先看看dequeueBuffer獲取BufferSlot的具體規則是什麼?
調用的是生產者BufferQueueProducerdequeueBuffer函數:

BufferQueueProducer::dequeueBuffer

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 scope
        std::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 scope

    BQ_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 scope
        std::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 requested
        usage |= mCore->mConsumerUsageBits;

        const bool useDefaultSize = !width && !height;
        if (useDefaultSize) {
            width = mCore->mDefaultWidth;
            height = mCore->mDefaultHeight;
        }
        ......

這個函數代碼也非常多,我們分部分來看,mCore->mIsAbandoned代表當前BufferQueue的狀態,如果不可用直接return,mCore->mConnectedApi這個是從Surface那邊傳遞獲取的,是否與BufferQueue建立連接,通常是調用native_window_api_connect這個API實現的,如果沒有建立連接也直接return,接着就是檢查寬高的合法性,定義好最終的返回值returnFlagsEGLDisplay,EGLSyncKHR暫時不清楚用來幹嘛的,接着來到如下段代碼:

if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
            mDequeueWaitingForAllocation = true;
            mCore->waitWhileAllocatingLocked(lock);
            mDequeueWaitingForAllocation = false;
            mDequeueWaitingForAllocationCondition.notify_all();
        }

mFreeBuffers前面已經分析過,代表所有綁定了GraphicBufferBufferSlot集合,並且BufferSlot狀態爲FREE,如果mFreeBuffers爲空(代表當前沒有綁定了GraphicBufferBufferSlot)並且當前正在爲GraphicBuffer分配內存空間,則調用waitWhileAllocatingLocked等待空間分配,等待\喚醒機制利用的是C++的條件變量,接着如果format == 0則使用mDefaultBufferFormat,接着如果寬高爲0則使用BufferQueueCore的默認寬高。

第一部分已經分析完了,接着看第二部分:

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) {
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
            if (status != NO_ERROR) {
                return status;
            }

            // This should not happen
            if (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.
            if (!mCore->mAllowAllocation) {
                if (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;
                }
            }
        }
	......
}

這第二部分就是尋找可用BufferSlot的核心代碼了,通過一個while循環不斷的查找,直到found != BufferItem::INVALID_BUFFER_SLOT,首先found初始化狀態爲BufferItem::INVALID_BUFFER_SLOT,進入While循環,調用核心函數waitForFreeSlotThenRelock,這個函數代碼也挺多的,把這個函數分三部分來分析,首先看第一部分。

waitForFreeSlotThenRelock

status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {
    auto callerString = (caller == FreeSlotCaller::Dequeue) ?
            "dequeueBuffer" : "attachBuffer";
    bool tryAgain = true;
    while (tryAgain) {
        if (mCore->mIsAbandoned) {
            BQ_LOGE("%s: BufferQueue has been abandoned", callerString);
            return NO_INIT;
        }

        int dequeuedCount = 0;
        int acquiredCount = 0;
        for (int s : mCore->mActiveBuffers) {
            if (mSlots[s].mBufferState.isDequeued()) {
                ++dequeuedCount;
            }
            if (mSlots[s].mBufferState.isAcquired()) {
                ++acquiredCount;
            }
        }

        // Producers are not allowed to dequeue more than
        // mMaxDequeuedBufferCount buffers.
        // This check is only done if a buffer has already been queued
        if (mCore->mBufferHasBeenQueued &&
                dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
            // Supress error logs when timeout is non-negative.
            if (mDequeueTimeout < 0) {
                BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
                        "count (%d)", callerString,
                        mCore->mMaxDequeuedBufferCount);
            }
            return INVALID_OPERATION;
        }

        *found = BufferQueueCore::INVALID_BUFFER_SLOT;
       	 ......
     }
     return NO_ERROR;

這整個函數就是一個while循環,直到找到可用BufferSlot或者發生錯誤,首先獲取當前調用此函數的是dequeueBuffer還是attachBuffer,定義while的開關tryAgain默認爲true,會一直嘗試獲取,進入while循環,如果當前BufferQueue已經被棄用則直接return,
接着看看如下小段代碼:

		int dequeuedCount = 0;
        int acquiredCount = 0;
        for (int s : mCore->mActiveBuffers) {
            if (mSlots[s].mBufferState.isDequeued()) {
                ++dequeuedCount;
            }
            if (mSlots[s].mBufferState.isAcquired()) {
                ++acquiredCount;
            }
        }

定義了兩個值dequeuedCountacquiredCount分別用來統計mActiveBuffers(所有綁定了GraphicBufferBufferSlot集合,並且狀態不爲FREEBufferSlot)中,狀態爲DEQUEUED和狀態爲ACQUIREDBufferSlot的數量。
接着繼續看一小段代碼:

	if (mCore->mBufferHasBeenQueued &&
                dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
            // Supress error logs when timeout is non-negative.
            if (mDequeueTimeout < 0) {
                BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
                        "count (%d)", callerString,
                        mCore->mMaxDequeuedBufferCount);
            }
            return INVALID_OPERATION;
        }

這段代碼主要是檢查生產者不允許申請超過mMaxDequeuedBufferCount數量的BufferSlot

接着看第二部分代碼:

status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {

		......
		const int maxBufferCount = mCore->getMaxBufferCountLocked();
        bool tooManyBuffers = mCore->mQueue.size()
                            > static_cast<size_t>(maxBufferCount);
        if (tooManyBuffers) {
            BQ_LOGV("%s: queue size is %zu, waiting", callerString,
                    mCore->mQueue.size());
        } else {
            // If in shared buffer mode and a shared buffer exists, always
            // return it.
            if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
                    BufferQueueCore::INVALID_BUFFER_SLOT) {
                *found = mCore->mSharedBufferSlot;
            } else {
                if (caller == FreeSlotCaller::Dequeue) {
                    // If we're calling this from dequeue, prefer free buffers
                    int slot = getFreeBufferLocked();
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;
                    } else if (mCore->mAllowAllocation) {
                        *found = getFreeSlotLocked();
                    }
                } else {
                    // If we're calling this from attach, prefer free slots
                    int slot = getFreeSlotLocked();
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;
                    } else {
                        *found = getFreeBufferLocked();
                    }
                }
            }
        }

       ......

    return NO_ERROR;
}

首先獲取BufferQueue最大buffer數量,一般爲2或者3,如果當前mQueue的大小已經大於最大buffer數量則應該等待buffer的消費,mQueue的大小會在queueBuffer時增加,queueBuffer這個函數在下一篇文章分析,接着看else分支:
首先如果當前使用的是共享buffer模式,並且mSharedBufferSlot是有效的,則直接返回mCore->mSharedBufferSlot,關於共享buffer模式不太瞭解,接着如果不是共享buffer模式:調用者是dequeueBuffer函數則首先調用getFreeBufferLockedmFreeBuffers頭部獲取BufferSlot,這段代碼也比較簡單:

int BufferQueueProducer::getFreeBufferLocked() const {
    if (mCore->mFreeBuffers.empty()) {
        return BufferQueueCore::INVALID_BUFFER_SLOT;
    }
    int slot = mCore->mFreeBuffers.front();
    mCore->mFreeBuffers.pop_front();
    return slot;
}

mFreeBuffers代表所有綁定了GraphicBufferBufferSlot集合,並且BufferSlot狀態爲FREE。如果從mFreeBuffers能找到可用BufferSlot並且是有效的則說明已經找到了,就給found賦值,接着如果沒有找到則進入else if分支,代表當前沒有綁定了GraphicBufferBufferSlot,需要重新給GraphicBuffer分配內存空間,所以else if的條件是當前是否允許分配空間,接着看getFreeSlotLocked函數:

int BufferQueueProducer::getFreeSlotLocked() const {
    if (mCore->mFreeSlots.empty()) {
        return BufferQueueCore::INVALID_BUFFER_SLOT;
    }
    int slot = *(mCore->mFreeSlots.begin());
    mCore->mFreeSlots.erase(slot);
    return slot;
}

這個函數也比較簡單,如果mFreeSlots不爲空就獲取mFreeSlots第一個元素,獲取之後從mFreeSlots刪除,mFreeSlots代表所有沒有綁定GraphicBufferBufferSlot集合,並且BufferSlot狀態爲FREE

好了else分支分析完了,我們可以知道獲取BufferSlot的規律了,會優先從mFreeBuffers中尋找,找不到再從mFreeSlots尋找。

接着來看waitForFreeSlotThenRelock最後一部分代碼:

status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {
      	......
      		 // If no buffer is found, or if the queue has too many buffers
        // outstanding, wait for a buffer to be acquired or released, or for the
        // max buffer count to change.
        tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
                   tooManyBuffers;
        if (tryAgain) {
            // Return an error if we're in non-blocking mode (producer and
            // consumer are controlled by the application).
            // However, the consumer is allowed to briefly acquire an extra
            // buffer (which could cause us to have to wait here), which is
            // okay, since it is only used to implement an atomic acquire +
            // release (e.g., in GLConsumer::updateTexImage())
            if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
                    (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
                return WOULD_BLOCK;
            }
            if (mDequeueTimeout >= 0) {
                std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
                        std::chrono::nanoseconds(mDequeueTimeout));
                if (result == std::cv_status::timeout) {
                    return TIMED_OUT;
                }
            } else {
                mCore->mDequeueCondition.wait(lock);
            }
        }
    } // while (tryAgain)
      	......  
      }

注意這三部分都是在while循環中進行的,第二部分我們已經找到了BufferSlot,此時的BufferSlot有兩種情況(不考慮共享buffer模式),一種是從mFreeBuffers中獲取的已經綁定了GraphicBuffer,另一種是從mFreeSlots中獲取的還沒有綁定GraphicBuffer,首先來看tryAgain的賦值,如果前面沒有獲取到有效BufferSlot或者tooManyBufferstrue則需要tryAgain繼續嘗試獲取,其實最後如果真的需要tryAgain更多是當前queue的buffer已經太多了,需要等待消費者acquire(非同步模式的情況,即mAsyncMode爲false),到這裏waitForFreeSlotThenRelock函數已經分析完了,這個函數代碼多其實邏輯很簡單,就是一個獲取BufferSlot的規則,先從mFreeBuffers中獲取,再從mFreeSlots中獲取。

接着我們再回到dequeueBuffer函數的第二部分:

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) {
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
            if (status != NO_ERROR) {
                return status;
            }

            // This should not happen
            if (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.
            if (!mCore->mAllowAllocation) {
                if (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;
                }
            }
        }
	......
}

waitForFreeSlotThenRelock函數之後已經獲取到了可用的BufferSlot(再強調一遍,獲取的BufferSlot都是下標index),接着根據獲取到的BufferSlot下標從mSlots中拿到BufferSlot,並通過BufferSlot綁定的GraphicBuffer創建一個
GraphicBuffer,注意此時BufferSlot綁定的GraphicBuffer有可能爲空的,接着判斷當前BufferQueue是否允許分配空間,如果不允許,並且當前GraphicBuffer需要分配空間(即waitForFreeSlotThenRelock函數是從mFreeSlots中獲取到的BufferSlot),共享buffer模式不管,那麼就重新將當前這個BufferSlot再插入mFreeSlots中,並且清空當前這個BufferSlot的各種狀態,然後將found再置爲BufferItem::INVALID_BUFFER_SLOT,並再進入下次循環,好了dequeueBuffer第二部分已經分析完了,總結:這部分其實就是在while循環中不斷的獲取BufferSlot,直到
found != BufferItem::INVALID_BUFFER_SLOT爲止,

接着看dequeueBuffer函數的第三部分:

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) {
		......
        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();

        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;

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

        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
		......
	
}

首先根據found獲取到BufferSlot,根據BufferSlotGraphicBuffer創建GraphicBufferBufferSlotGraphicBuffer有可能爲空,共享buffer模式跳過,如果found不是mSharedBufferSlot,則將found插入mActiveBuffersmActiveBuffers代表所有綁定了GraphicBufferBufferSlot集合,並且BufferSlot狀態不爲FREE,可以是另外四種狀態的任意一種。雖說mActiveBuffers中的BufferSlot一定綁定了GraphicBuffer,但此時此刻,BufferSlotGraphicBuffer任然可能是空的,繼續看代碼,這個outSlot是最終返回給Surface那一端的,接着獲取當前foundBufferSlotmNeedsReallocation,是否需要重新分配空間,然後再修改爲false,接着修改當前foundBufferSlot的狀態爲DEQUEUED,然後我們再看接下來一小段代碼:

		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;

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

如果foundBufferSlot並沒有綁定GraphicBuffer或者GraphicBuffer需要分配空間則將foundBufferSlot的狀態全部clear,置爲初始狀態值,否則將BufferQueueCoremBufferAge +1,mFrameCountermFrameNumber一般是相等的,代表當前queue的buffer數量,eglDisplayeglFence不太清楚具體用處,暫時跳過,這部分最後也是和共享buffer模式有關,跳過。

接着我們來看dequeue最後一部分代碼:

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) {
		......
		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 scope
            std::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;
}

首先判斷如果GraphicBuffer需要分配空間,則new一個GraphicBuffer,關於GraphicBuffer分配空間的內容比較複雜(需要使用Gralloc HAL進行內存分配),本篇文章就不看了,new了之後會通過mIsAllocatingCondition.notify_all喚醒如果有因爲mIsAllocating陷入等待的線程,還會將分配好的graphicBufferBufferSlot進行綁定,如果分配空間失敗便會將BufferSlot插入mFreeSlots中,並通過clearBufferSlotLocked函數情況BufferSlot狀態,如果BufferQueue當前已經被棄用則同樣將BufferSlot插入mFreeSlots中並清空狀態,接着如果attachedByConsumer爲true,則添加上BUFFER_NEEDS_REALLOCATION的flag,eglDisplay, eglFence不清楚,暫時跳過,剩下的代碼就不看了。

到此dequeueBuffer函數已經分析完畢,雖然代碼很多,也分析了很多,但這個函數的核心就是申請BufferSlot,先從mFreeBuffers取,沒取到再從mFreeSlots取,然後再看取到的BufferSlot是否綁定了GraphicBuffer,如果沒有則還需要爲GraphicBuffer分配內存空間,其實有很多的代碼都是對異常情況的處理。

dequeueBuffer函數執行完畢之後,Surface一端就拿到了可用的BufferSlot的下標。

我們再接着看SurfacedequeueBuffer函數的剩下部分:

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
	......
	sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
	...
	
    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;
        }
    }
    *buffer = gbuf.get();
    return OK;
}

最後部分就選了上面的代碼來看,首先Surface這一端創建GraphicBuffer指向BufferQueueProducer獲取到的BufferSlot綁定的GraphicBuffer,接着如果剛剛dequeueBuffer函數返回的result爲BUFFER_NEEDS_REALLOCATION,或者gbuf爲空,則調用requestBuffer函數到BufferQueueProducer一端重新給BufferSlot綁定GraphicBuffer,代碼就不看了,很簡單,最後將gbuf給到*buffer返回給最終的應用層,拿到GraphicBuffer之後就可以進行各種繪製操作了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章