Android P 圖形顯示系統(tǒng)(十二) BufferQueue(三)

Buffer狀態(tài)

對(duì)于生產(chǎn)者這邊,BufferQueue的流程基本講完了。簡(jiǎn)單說(shuō)來(lái)时呀,首先提需求张漂,告訴BufferQueue需要什么樣的Buffer,大小谨娜,格式航攒,usage等等;然后dequeue Buffer出來(lái)趴梢,往Buffer里面繪制顯示數(shù)據(jù)漠畜;繪制完成后,queue到BufferQueue里面坞靶,并通知消費(fèi)者進(jìn)行消費(fèi)憔狞。如此不斷的的dequeue,繪制彰阴,queue瘾敢。

消費(fèi)者這邊的流程,我們還沒(méi)有講到尿这。對(duì)于消費(fèi)者來(lái)說(shuō)簇抵,收到通知后,將從BufferQueue里面取queue過(guò)來(lái)的Buffer進(jìn)行合成射众,合成完的Buffer再釋放掉正压,這里的釋放,是概念上的责球,并沒(méi)有真正釋放內(nèi)存焦履,只是讓其返回隊(duì)列,可以被再次dequeue雏逾。消費(fèi)者這邊也是不斷的接通知嘉裤,取buffer合成,然后釋放栖博,不斷循環(huán)屑宠。
此圖是Android官網(wǎng)對(duì)BufferQueue通信過(guò)程的描述,這很好的描述這個(gè)過(guò)程仇让。


BufferQueue數(shù)據(jù)流

在Android 6.0及之前的版本典奉,在這些通信過(guò)程中,都將Buffer的狀態(tài)標(biāo)記為具體的狀態(tài)丧叽。這四個(gè)過(guò)程Buffer分別對(duì)應(yīng)不同的四個(gè)狀態(tài):

  • DEQUEUED 狀態(tài)
    Producer dequeue一個(gè)Buffer后卫玖,這個(gè)Buffer就變?yōu)镈EQUEUED狀態(tài),release Fence發(fā)信號(hào)后踊淳,Producer就可以修改Buffer的內(nèi)容假瞬,我們稱為release Fence。此時(shí)Buffer被Producer占用。DEQUEUED狀態(tài)的Buffer可以遷移到 QUEUED 狀態(tài)脱茉,通過(guò)queueBuffer或attachBuffer流程剪芥。也可以遷移到FREE裝,通過(guò)cancelBuffer或detachBuffer流程琴许。

  • QUEUED 狀態(tài)
    Buffer繪制完后税肪,queue到BufferQueue中,給Consumer進(jìn)行消費(fèi)榜田。此時(shí)Buffer可能還沒(méi)有真正繪制完成益兄,必現(xiàn)要等對(duì)應(yīng)的Fence發(fā)信號(hào)出來(lái)后,才真正完成串慰。此時(shí)Buffer是BufferQueue持有,可以遷移到ACQUIRED狀態(tài)唱蒸,通過(guò)acquireBuffer流程邦鲫。而已可以遷移到FREE狀態(tài),如果另外一個(gè)Buffer被異步的queue進(jìn)來(lái)神汹。

  • ACQUIRED 狀態(tài)
    Buffer已經(jīng)被Consumer獲取庆捺,但是也必須要等對(duì)應(yīng)的Fence發(fā)信號(hào)才能被Consumer讀寫(xiě),找個(gè)Fence是從Producer那邊屁魏,queueBuffer的時(shí)候傳過(guò)來(lái)的滔以。我們將其稱為acquire fence。此時(shí)氓拼,Buffer被Consumer持有你画。狀態(tài)可以遷移到FREE狀態(tài),通過(guò)releaseBuffer或detachBuffer流程桃漾。除了從acquireBuffer流程可以遷移到ACQUIRED狀態(tài)坏匪,attachBuffer流程也可以遷移到ACQUIRED狀態(tài)。

  • FREE 狀態(tài)
    FREE狀態(tài)撬统,說(shuō)明Buffer被BufferQueue持有适滓,可以被Producer dequeue,它將遷移到DEQUEUED狀態(tài)恋追,通過(guò)dequeueBuffer流程凭迹。

  • SHARED狀態(tài)
    SHARED狀態(tài)是一個(gè)特殊的狀態(tài),SHARED的Buffer并不參與前面所說(shuō)的狀態(tài)遷移苦囱。它說(shuō)明Buffer被用與共享Buffer模式嗅绸。除了FREE狀態(tài),它可以是其他的任何狀態(tài)撕彤。它可以被多次dequeued, queued, 或者 acquired朽砰。這中共享Buffer的模式,主要用于VR等低延遲要求的場(chǎng)合。

目前瞧柔,Buffer的狀態(tài)漆弄,都是通過(guò)各個(gè)狀態(tài)的Buffer的量來(lái)表示狀態(tài),對(duì)應(yīng)的關(guān)系如下:

Buffer狀態(tài) 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

Buffer的狀態(tài)在代碼中用BufferState描述造锅,BufferState的定義如下:

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

struct BufferState {

    BufferState()
    : mDequeueCount(0),
      mQueueCount(0),
      mAcquireCount(0),
      mShared(false) {
    }

    uint32_t mDequeueCount;
    uint32_t mQueueCount;
    uint32_t mAcquireCount;
    bool mShared;

    ... ...
};

前面講解dequeueBuffer和queueBuffer流程時(shí)撼唾,BufferQueue有很多個(gè)隊(duì)列,我們?cè)賮?lái)看一下BufferQueue中哥蔚,幾個(gè)隊(duì)列間的關(guān)系倒谷。

BufferQueueCore中的定義如下:

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

class BufferQueueCore : public virtual RefBase {

    ... ...

    typedef Vector<BufferItem> Fifo;
    
    ... ...

    // mSlots is an array of buffer slots that must be mirrored on the producer
    // side. This allows buffer ownership to be transferred between the producer
    // and consumer without sending a GraphicBuffer over Binder. The entire
    // array is initialized to NULL at construction time, and buffers are
    // allocated for a slot when requestBuffer is called with that slot's index.
    BufferQueueDefs::SlotsType mSlots;

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

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

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

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

    // mActiveBuffers contains all slots which have a non-FREE buffer attached.
    std::set<int> mActiveBuffers;
  • mSlots
    mSlots 是Buffer序號(hào)的一個(gè)數(shù)組,Producer端的mSlots也是這個(gè)mSlots糙箍,Consumer端是mSlots也是里的mSlots的引用渤愁。它可實(shí)現(xiàn)Buffer在Producer和Consumer之間轉(zhuǎn)移,而不需要真正的在Binder間去傳輸一個(gè)GraphicBuffer深夯。初始狀態(tài)時(shí)為空抖格,當(dāng)requestBuffer流程執(zhí)行時(shí),將去為對(duì)應(yīng)的Buffer序號(hào)咕晋,分配真正的Buffer雹拄。

  • mQueue
    mQueue是一個(gè)先進(jìn)先出的Vector,是同步模式下使用掌呜。里面就是處于QUEUED狀態(tài)的Buffer滓玖。

  • mFreeSlots
    mFreeSlots包含所有是FREE狀態(tài),且還沒(méi)有分配Buffer的质蕉,Buffer序號(hào)集合势篡。剛開(kāi)始時(shí),mFreeSlots被初始化為MaxBufferCount個(gè)Buffer序號(hào)集合模暗,dequeueBuffer的時(shí)候殊霞,將先從這個(gè)集合中獲取。但是消費(fèi)者消費(fèi)完成汰蓉,釋放的Buffer并不返回到這個(gè)隊(duì)列中绷蹲,而是返回到mFreeBuffers中。

  • mFreeBuffers
    mFreeBuffers包含的是所有FREE狀態(tài)顾孽,且已經(jīng)分配Buffer的祝钢,Buffer序號(hào)的結(jié)合。消費(fèi)者消費(fèi)完成若厚,釋放的Buffer并不返回到這個(gè)隊(duì)列中拦英,而是返回到mFreeBuffers中。

  • mUnusedSlots
    mUnusedSlots和mFreeSlots有些相似测秸,只是mFreeSlots會(huì)被用到疤估,而mUnusedSlots中的Buffer序號(hào)不會(huì)不用到灾常。也就是,總的Buffer序號(hào)NUM_BUFFER_SLOTS中铃拇,除去MaxBufferCount個(gè)mFreeSlots钞瀑,剩余的集合。

  • mActiveBuffers
    mActiveBuffers包含所有非FREE狀態(tài)的Buffer慷荔。也就是包含了DEQUEUED雕什,QUEUED,ACQUIRED以及SHARED這幾個(gè)狀態(tài)的显晶。

我們從數(shù)學(xué)的角度來(lái)看看他們之間的關(guān)系:
mSlots的數(shù)組大小為NUM_BUFFER_SLOTS贷岸,但是其中,真正用起來(lái)的也只有MaxBufferCount個(gè)磷雇,其他的都不會(huì)被用到偿警。所以,我們可以這么理解唯笙,mSlots是BufferQueue中實(shí)際流轉(zhuǎn)起來(lái)的Buffer螟蒸。

mSlots = mFreeBuffers + mActiveBuffers

對(duì)于整體而言:

NUM_BUFFER_SLOTS = mUnusedSlots + mFreeSlots + mFreeBuffers + mActiveBuffers

mSlots是BufferSlot的集合,BufferSlot定義如下:

struct BufferSlot {

    BufferSlot()
    : mGraphicBuffer(nullptr),
      mEglDisplay(EGL_NO_DISPLAY),
      mBufferState(),
      mRequestBufferCalled(false),
      mFrameNumber(0),
      mEglFence(EGL_NO_SYNC_KHR),
      mFence(Fence::NO_FENCE),
      mAcquireCalled(false),
      mNeedsReallocation(false) {
    }

    // Buffer序號(hào)對(duì)應(yīng)的Buffer
    sp<GraphicBuffer> mGraphicBuffer;

    // 創(chuàng)建EGLSyncKHR對(duì)象用
    EGLDisplay mEglDisplay;

    // Buffer序號(hào)當(dāng)前的狀態(tài)
    BufferState mBufferState;

    // mRequestBufferCalled 表示Producer確實(shí)已經(jīng)調(diào)用requestBuffer
    bool mRequestBufferCalled;

    // mFrameNumber 表示該Buffer序號(hào)已經(jīng)被queue的次數(shù).  主要用于dequeueBuffer時(shí)睁本,遵從LRU尿庐,這很有用忠怖,因?yàn)閎uffer 變FREE時(shí)呢堰,可能release Fence還沒(méi)有發(fā)信號(hào)出來(lái)。
    uint64_t mFrameNumber;

    // 現(xiàn)在已經(jīng)被mFence替換了凡泣,基本不用
    EGLSyncKHR mEglFence;

    // mFence 是同步的一種方式枉疼,上一個(gè)owner使用完Buffer后,需要發(fā)信號(hào)出來(lái)鞋拟,下一個(gè)owner才可以使用骂维。
    sp<Fence> mFence;

    // 表示Buffer已經(jīng)被Consumer取走
    bool mAcquireCalled;

    // 表示Buffer需要重新分配,需要設(shè)置BUFFER_NEEDS_REALLOCATION 通知Producer贺纲,不要用原來(lái)的緩存的Buffer
    bool mNeedsReallocation;
};

看完Buffer的狀態(tài)后航闺,再回頭去看看前面介紹的dequeueBuffer和queueBuffer,是不是就很好理解了猴誊。

我們?cè)賮?lái)看看BufferQueue的工作模式潦刃,BufferQueue可以工作在幾個(gè)模式:

  • 同步模式 Synchronous-like mode
    默認(rèn)情況下,BufferQueue將工作在同步模式下懈叹。在該模式下乖杠,每個(gè)Buffer都從Producer進(jìn)入,從Consumer退出澄成,沒(méi)有Buffer沒(méi)有丟棄掉胧洒。如果Producer生產(chǎn)的太快畏吓,Consumer來(lái)不及消費(fèi),Producer將阻塞等待FREE的Buffer卫漫。前面的分析流程的時(shí)候在waitForFreeSlotThenRelock也說(shuō)到了這點(diǎn)菲饼。
    這是waitForFreeSlotThenRelock函數(shù)中的邏輯:
            if (mDequeueTimeout >= 0) {
                status_t result = mCore->mDequeueCondition.waitRelative(
                        mCore->mMutex, mDequeueTimeout);
                if (result == TIMED_OUT) {
                    return result;
                }
            } else {
                mCore->mDequeueCondition.wait(mCore->mMutex);
            }
  • 非同步模式 Non-blocking mode
    和同步模式相反,BufferQueue工作在非阻塞模式下汛兜,在這種模式下巴粪,如果沒(méi)有FREE Buffer,將生成一個(gè)錯(cuò)誤粥谬,而不是阻塞等待FREE的Buffer肛根。這種模式,也沒(méi)有Buffer不丟棄漏策。這中模式可以避免潛在的死鎖派哲,如果應(yīng)用不理解Graphics框架中復(fù)雜的依賴條件。前面我們的代碼分析中也看到這一點(diǎn)掺喻。waitForFreeSlotThenRelock什么時(shí)候不去tryAgain芭届?
        if (tryAgain) {
            if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
                    (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
                return WOULD_BLOCK;
            }

mAsyncMode是通過(guò)BufferQueueProducer的setAsyncMode函數(shù)設(shè)置的,從Producer調(diào)用過(guò)來(lái)感耙,受Producer控制褂乍。

mDequeueBufferCannotBlock則是在Producer 連接到BufferQueue時(shí),根據(jù)條件判斷的即硼,具體邏輯如下:

status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
        int api, bool producerControlledByApp, QueueBufferOutput *output) {
    ... ...

    if (mDequeueTimeout < 0) {
        mCore->mDequeueBufferCannotBlock =
                mCore->mConsumerControlledByApp && producerControlledByApp;
    }

    mCore->mAllowAllocation = true;
    VALIDATE_CONSISTENCY();
    return status;
}
  • 舍棄模式 Discard mode
    BufferQueue可以配置為丟棄舊Buffer逃片,而不是生成錯(cuò)誤或進(jìn)行等待。比如只酥,如果用GL對(duì)紋理進(jìn)行快速的繪制褥实,那么舊的Buffer不要丟棄。

  • 共享Buffer模式 shared buffer mode
    共享Buffer模式裂允,表示Buffer是Producer和Consumer共享损离。共享Buffer模式下,一直用的都是同一個(gè)Buffer绝编。而B(niǎo)uffer的狀態(tài)不能遷移為FREE狀態(tài)僻澎。代碼中可以留意mCore->mSharedBufferModemCore->mSharedBufferSlot。這個(gè)模式其實(shí)也包含在同步模式中十饥,只是比較特殊窟勃,單獨(dú)說(shuō)一下。

現(xiàn)在绷跑,再回頭去看看前面介紹的dequeueBuffer和queueBuffer拳恋,是不是就更好理解了。

acquireBuffer流程

Buffer queue到BufferQueue中后砸捏,將通知消費(fèi)者去消費(fèi)谬运。消費(fèi)時(shí)隙赁,通過(guò)acquireBuffer來(lái)獲取Buffer,我們且不管acquireBuffer是什么地方調(diào)的梆暖,我們先來(lái)看BufferQueue中acquireBuffer的處理流程伞访。

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

status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
        nsecs_t expectedPresent, uint64_t maxFrameNumber) {
    ATRACE_CALL();

    int numDroppedBuffers = 0;
    sp<IProducerListener> listener;
    {
        Mutex::Autolock lock(mCore->mMutex);

        int numAcquiredBuffers = 0;
        for (int s : mCore->mActiveBuffers) {
            if (mSlots[s].mBufferState.isAcquired()) {
                ++numAcquiredBuffers;
            }
        }
        if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
            BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
                    numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
            return INVALID_OPERATION;
        }

        bool sharedBufferAvailable = mCore->mSharedBufferMode &&
                mCore->mAutoRefresh && mCore->mSharedBufferSlot !=
                BufferQueueCore::INVALID_BUFFER_SLOT;

        // In asynchronous mode the list is guaranteed to be one buffer deep,
        // while in synchronous mode we use the oldest buffer.
        if (mCore->mQueue.empty() && !sharedBufferAvailable) {
            return NO_BUFFER_AVAILABLE;
        }
  • acquireBuffer時(shí),也是受mCore->mMutex控制的轰驳。
  • numAcquiredBuffers厚掷,已經(jīng)acquired的Buffer。mMaxAcquiredBufferCount最大可以acquire的Buffer级解,可以溢出一個(gè)冒黑,以便Consumer能方便替換舊的Buffer,如果舊的Buffer還沒(méi)有釋放時(shí)勤哗。
  • sharedBufferAvailable抡爹,共享Buffer模式下使用。在這個(gè)模式下芒划,mAutoRefresh表示冬竟,Consumer永遠(yuǎn)可以acquire到一塊Buffer,即使BufferQueue還沒(méi)有處于可以acquire的狀態(tài)民逼。
  • mQueue泵殴,如沒(méi)有Buffer被queue過(guò)來(lái),mQueue為空拼苍,那么Consumer這邊就acquire不到新的Buffer笑诅,Consumer這邊已經(jīng)acquire的會(huì)被繼續(xù)使用。

如果有Buffer或是共享Buffer模式映屋,繼續(xù)~

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

        BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());

        if (expectedPresent != 0 && !mCore->mQueue.empty()) {
            const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second

            while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
                const BufferItem& bufferItem(mCore->mQueue[1]);

                // If dropping entry[0] would leave us with a buffer that the
                // consumer is not yet ready for, don't drop it.
                if (maxFrameNumber && bufferItem.mFrameNumber > maxFrameNumber) {
                    break;
                }

                if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
                        desiredPresent > expectedPresent) {
                    // This buffer is set to display in the near future, or
                    // desiredPresent is garbage. Either way we don't want to drop
                    // the previous buffer just to get this on the screen sooner.
                    BQ_LOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%"
                            PRId64 " (%" PRId64 ") now=%" PRId64,
                            desiredPresent, expectedPresent,
                            desiredPresent - expectedPresent,
                            systemTime(CLOCK_MONOTONIC));
                    break;
                }

                BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
                        " size=%zu",
                        desiredPresent, expectedPresent, mCore->mQueue.size());

                if (!front->mIsStale) {
                    // Front buffer is still in mSlots, so mark the slot as free
                    mSlots[front->mSlot].mBufferState.freeQueued();

                    if (!mCore->mSharedBufferMode &&
                            mSlots[front->mSlot].mBufferState.isFree()) {
                        mSlots[front->mSlot].mBufferState.mShared = false;
                    }

                    // Don't put the shared buffer on the free list
                    if (!mSlots[front->mSlot].mBufferState.isShared()) {
                        mCore->mActiveBuffers.erase(front->mSlot);
                        mCore->mFreeBuffers.push_back(front->mSlot);
                    }

                    listener = mCore->mConnectedProducerListener;
                    ++numDroppedBuffers;
                }

                mCore->mQueue.erase(front);
                front = mCore->mQueue.begin();
            }

            bool bufferIsDue = desiredPresent <= expectedPresent ||
                    desiredPresent > expectedPresent + MAX_REASONABLE_NSEC;
            bool consumerIsReady = maxFrameNumber > 0 ?
                    front->mFrameNumber <= maxFrameNumber : true;
            if (!bufferIsDue || !consumerIsReady) {

                return PRESENT_LATER;
            }

        }

這里主要做了一些幾件事:

  • expectedPresent 期望被顯示的時(shí)間
    也就是這個(gè)Buffer希望在什么時(shí)候被顯示到屏幕上苟鸯。如果Buffer的DesiredPresent的時(shí)間早于這個(gè)時(shí)間同蜻,那么這個(gè)Buffer將被準(zhǔn)時(shí)顯示棚点。或者稍晚才被顯示湾蔓,如果我們不想顯示直到expectedPresent時(shí)間之后瘫析,我們返回PRESENT_LATER,不去acquire它默责。但是如果時(shí)間在一秒之內(nèi)贬循,就不會(huì)延遲了,直接acquire回去桃序。
  • 檢查是否需要丟棄一些幀
    如果是Surface自動(dòng)生成的時(shí)間杖虾,就不去檢查是否需要丟棄掉一些幀,這些Surface對(duì)顯示時(shí)間是沒(méi)有嚴(yán)格的要求的媒熊。如果mQueue中有多個(gè)Buffer奇适,我們將丟掉一些queue過(guò)來(lái)比較早的Buffer坟比。如果最近queue的Buffer,離期望顯示的時(shí)間已經(jīng)沒(méi)有一秒了嚷往,那之前queue過(guò)來(lái)的Buffer都將被丟棄掉葛账。這很好理解,你好比你要買一款手機(jī)皮仁,新款的廣告雖然來(lái)了籍琳,但是還有一段時(shí)間才能上市,你等不了這么就久贷祈,就先買就舊款了趋急,總得用手機(jī)吧。但是势誊,如果新款不到一秒就上市了宣谈,我們就稍微等會(huì)兒直接買新款,不買舊款了键科。
    front->mIsStale闻丑,表示Buffer已經(jīng)被釋放了,這是在BufferQueueCore::freeAllBuffersLocked時(shí)置的位勋颖。此時(shí)嗦嗡,我們需要將Buffer都返回到BufferQueue FREE狀態(tài)中。

該丟棄的丟棄了饭玲,余下的就可以用來(lái)去顯示了侥祭。

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

        int slot = BufferQueueCore::INVALID_BUFFER_SLOT;

        if (sharedBufferAvailable && mCore->mQueue.empty()) {
            // make sure the buffer has finished allocating before acquiring it
            mCore->waitWhileAllocatingLocked();

            slot = mCore->mSharedBufferSlot;

            // Recreate the BufferItem for the shared buffer from the data that
            // was cached when it was last queued.
            outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
            outBuffer->mFence = Fence::NO_FENCE;
            outBuffer->mFenceTime = FenceTime::NO_FENCE;
            outBuffer->mCrop = mCore->mSharedBufferCache.crop;
            outBuffer->mTransform = mCore->mSharedBufferCache.transform &
                    ~static_cast<uint32_t>(
                    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
            outBuffer->mScalingMode = mCore->mSharedBufferCache.scalingMode;
            outBuffer->mDataSpace = mCore->mSharedBufferCache.dataspace;
            outBuffer->mFrameNumber = mCore->mFrameCounter;
            outBuffer->mSlot = slot;
            outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled;
            outBuffer->mTransformToDisplayInverse =
                    (mCore->mSharedBufferCache.transform &
                    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
            outBuffer->mSurfaceDamage = Region::INVALID_REGION;
            outBuffer->mQueuedBuffer = false;
            outBuffer->mIsStale = false;
            outBuffer->mAutoRefresh = mCore->mSharedBufferMode &&
                    mCore->mAutoRefresh;
        } else {
            slot = front->mSlot;
            *outBuffer = *front;
        }

如果是共享Buffer模式,即使mQueue為空茄厘,也會(huì)把共享的Buffer返回去矮冬。其他情況下就返回,mQueue的第一個(gè)Buffer次哈。

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

        ATRACE_BUFFER_INDEX(slot);

        if (!outBuffer->mIsStale) {
            mSlots[slot].mAcquireCalled = true;

            if (mCore->mQueue.empty()) {
                mSlots[slot].mBufferState.acquireNotInQueue();
            } else {
                mSlots[slot].mBufferState.acquire();
            }
            mSlots[slot].mFence = Fence::NO_FENCE;
        }

        if (outBuffer->mAcquireCalled) {
            outBuffer->mGraphicBuffer = NULL;
        }

        mCore->mQueue.erase(front);

        mCore->mDequeueCondition.broadcast();

        ATRACE_INT(mCore->mConsumerName.string(),
                static_cast<int32_t>(mCore->mQueue.size()));
        mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());

        VALIDATE_CONSISTENCY();
    }

    if (listener != NULL) {
        for (int i = 0; i < numDroppedBuffers; ++i) {
            listener->onBufferReleased();
        }
    }

    return NO_ERROR;
}

acquire到Buffer后胎署,修改mSlots中對(duì)應(yīng)Buffer序號(hào)的mBufferState狀態(tài)。acquire的Buffer窑滞,需要從mQueue中 刪掉琼牧。留意這里的ATRACE_INT,這個(gè)在systrace分析時(shí)哀卫,非常有用巨坊。如果Buffer被丟棄了,可以通過(guò)Producer的監(jiān)聽(tīng)者此改,去通知Producer Buffer已經(jīng)被release掉了趾撵。

releaseBuffer流程分析

Consumer具體怎么消費(fèi)的,我們暫時(shí)不管共啃,我們先來(lái)看消費(fèi)完成后占调,releaseBuffer的流程勋拟。

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

status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
        const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
        EGLSyncKHR eglFence) {
    ATRACE_CALL();
    ATRACE_BUFFER_INDEX(slot);

    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
            releaseFence == NULL) {
        BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
                releaseFence.get());
        return BAD_VALUE;
    }

    sp<IProducerListener> listener;
    { // Autolock scope
        Mutex::Autolock lock(mCore->mMutex);

        // FrameNumber已經(jīng)變,buffer已經(jīng)被重新分配
        if (frameNumber != mSlots[slot].mFrameNumber &&
                !mSlots[slot].mBufferState.isShared()) {
            return STALE_BUFFER_SLOT;
        }

        if (!mSlots[slot].mBufferState.isAcquired()) {
            BQ_LOGE("releaseBuffer: attempted to release buffer slot %d "
                    "but its state was %s", slot,
                    mSlots[slot].mBufferState.string());
            return BAD_VALUE;
        }

        mSlots[slot].mEglDisplay = eglDisplay;
        mSlots[slot].mEglFence = eglFence;
        mSlots[slot].mFence = releaseFence;
        mSlots[slot].mBufferState.release();

        if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
            mSlots[slot].mBufferState.mShared = false;
        }
        // Don't put the shared buffer on the free list.
        if (!mSlots[slot].mBufferState.isShared()) {
            mCore->mActiveBuffers.erase(slot);
            mCore->mFreeBuffers.push_back(slot);
        }

        listener = mCore->mConnectedProducerListener;
        BQ_LOGV("releaseBuffer: releasing slot %d", slot);

        mCore->mDequeueCondition.broadcast();
        VALIDATE_CONSISTENCY();
    } // Autolock scope

    // Call back without lock held
    if (listener != NULL) {
        listener->onBufferReleased();
    }

    return NO_ERROR;
}
  • release Buffer的流程相對(duì)簡(jiǎn)單妈候,slot就是需要釋放的Buffer的序號(hào)敢靡。
  • Buffer的FrameNumber變了,可能Buffer已經(jīng)重新分配苦银,這個(gè)是不用管啸胧。
  • 只能釋放acquire狀態(tài)的buffer序號(hào),釋放后是Buffer放會(huì)mFreeBuffers中幔虏。
  • releaseFence纺念,從Consumer那邊傳過(guò)來(lái),Producer可以Dequeue mFreeBuffers中的Buffer想括,但是只有releaseFence發(fā)信號(hào)出來(lái)后陷谱,Consumer才真正用完,Producer才可以寫(xiě)瑟蜈。
  • 同樣的烟逊,可以通過(guò)listener通知Producer。

就這么多~~

小結(jié)

本章主要通過(guò)測(cè)試應(yīng)用铺根,講解ANativeWindow宪躯,Surface間的關(guān)系,Surface和Producer位迂,Consumer間的關(guān)系访雪;P應(yīng)用怎么使用BufferQueue。講解了BufferQueue相關(guān)的幾個(gè)流程掂林,dequeueBuffer臣缀,queueBuffer,acquireBuffer泻帮,releaseBuffer精置;以及Buffer的狀態(tài),DEQUEUED刑顺,QUEUED氯窍,ACQUIRED饲常,F(xiàn)REE遷移蹲堂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市贝淤,隨后出現(xiàn)的幾起案子柒竞,更是在濱河造成了極大的恐慌,老刑警劉巖播聪,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朽基,死亡現(xiàn)場(chǎng)離奇詭異布隔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)靶端,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門寄悯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)顷编,“玉大人,你說(shuō)我怎么就攤上這事哀军。” “怎么了打却?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵杉适,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我柳击,道長(zhǎng)猿推,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任捌肴,我火速辦了婚禮蹬叭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘状知。我一直安慰自己具垫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布试幽。 她就那樣靜靜地躺著筝蚕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铺坞。 梳的紋絲不亂的頭發(fā)上起宽,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音济榨,去河邊找鬼坯沪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛擒滑,可吹牛的內(nèi)容都是我干的腐晾。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼丐一,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼藻糖!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起库车,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤巨柒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體洋满,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晶乔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了牺勾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片正罢。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驻民,靈堂內(nèi)的尸體忽然破棺而出腺怯,到底是詐尸還是另有隱情,我是刑警寧澤川无,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布呛占,位于F島的核電站,受9級(jí)特大地震影響懦趋,放射性物質(zhì)發(fā)生泄漏晾虑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一仅叫、第九天 我趴在偏房一處隱蔽的房頂上張望帜篇。 院中可真熱鬧,春花似錦诫咱、人聲如沸笙隙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)竟痰。三九已至,卻和暖如春掏呼,著一層夾襖步出監(jiān)牢的瞬間坏快,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工憎夷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留莽鸿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓拾给,卻偏偏與公主長(zhǎng)得像祥得,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒋得,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容