Android 12(S) 圖形顯示系統 - BufferQueue的工作流程(十一) Android 12(S) 圖形顯示系統 - 開篇
題外話
我竟然已經寫了這個系列的十一篇文章了,雖然內容很淺顯,雖然內容很枯燥,雖然內容也許沒營養,但我為自己的堅持點贊!
必讀:Android 12(S) 圖形顯示系統 - 開篇
一、前言
前面的兩篇文章,分別講解了Producer的處理邏輯和queue buffer後通過FrameAvailableListener通知到Consumer的基本過程。
流程已經走到了BufferQueueConsumer::acquireBuffer中,所以這篇文章聚焦Consumer的一些處理邏輯。
還是把流程圖貼上來
從流程圖中看,這篇文章就是講解右半部分的內容。
二、消費者-Consumer的相關邏輯
瞭解了 BufferQueueCore 和 BufferQueueProducer,接著看 BufferQueue 的最後一個元素:BufferQueueConsumer。
BufferQueueConsumer作為消費者的一個代表元素通過 acquireBuffer 來獲取影象緩衝區,通過 releaseBuffer 來釋放該緩衝區。
下面就分別看看 BufferQueueConsumer 中 acquireBuffer 和 releaseBuffer 兩個操作的具體流程。
2.1 程式碼位置
/frameworks/native/libs/gui/BufferQueueConsumer.cpp
2.2 acquireBuffer的邏輯
先看 acquireBuffer 的過程,上原始碼
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent, uint64_t maxFrameNumber) { ATRACE_CALL(); int numDroppedBuffers = 0; sp<IProducerListener> listener; { std::unique_lock<std::mutex> lock(mCore->mMutex); // Check that the consumer doesn't currently have the maximum number of // buffers acquired. We allow the max buffer count to be exceeded by one // buffer so that the consumer can successfully set up the newly acquired // buffer before releasing the old one. // 檢查acquire的buffer的數量是否超出了限制 int numAcquiredBuffers = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isAcquired()) { ++numAcquiredBuffers; } } const bool acquireNonDroppableBuffer = mCore->mAllowExtraAcquire && numAcquiredBuffers == mCore->mMaxAcquiredBufferCount + 1; if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1 && !acquireNonDroppableBuffer) { 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. // 檢查BufferQueueCore中的mQueue佇列是否為空 if (mCore->mQueue.empty() && !sharedBufferAvailable) { return NO_BUFFER_AVAILABLE; } // 獲取BufferQueueCore中的mQueue佇列的迭代器 BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); // If expectedPresent is specified, we may not want to return a buffer yet. // If it's specified and there's more than one buffer queued, we may want // to drop a buffer. // Skip this if we're in shared buffer mode and the queue is empty, // since in that case we'll just return the shared buffer. if (expectedPresent != 0 && !mCore->mQueue.empty()) { // expectedPresent表示期望這個buffer什麼時候顯示到螢幕上。 // 如果buffer的期望顯示時間小於expectedPresent,我們會acquire and return這個buffer // 如果我們不想顯示它直到expectedPresent之後,可以返回PRESENT_LATER // The 'expectedPresent' argument indicates when the buffer is expected // to be presented on-screen. If the buffer's desired present time is // earlier (less) than expectedPresent -- meaning it will be displayed // on time or possibly late if we show it as soon as possible -- we // acquire and return it. If we don't want to display it until after the // expectedPresent time, we return PRESENT_LATER without acquiring it. // // 安全起見,如果expectedPresent超過了buffer的期望顯示時間1秒,我們會推遲acquire // To be safe, we don't defer acquisition if expectedPresent is more // than one second in the future beyond the desired present time // (i.e., we'd be holding the buffer for a long time). // // NOTE: Code assumes monotonic time values from the system clock // are positive. // 檢查是否需要丟棄一些幀,主要是判斷timestamps & expectedPresent // Start by checking to see if we can drop frames. We skip this check if // the timestamps are being auto-generated by Surface. If the app isn't // generating timestamps explicitly, it probably doesn't want frames to // be discarded based on them. 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 entry[1] is timely, drop entry[0] (and repeat). We apply an // additional criterion here: we only drop the earlier buffer if our // desiredPresent falls within +/- 1 second of the expected present. // Otherwise, bogus desiredPresent times (e.g., 0 or a small // relative timestamp), which normally mean "ignore the timestamp // and acquire immediately", would cause us to drop frames. // // We may want to add an additional criterion: don't drop the // earlier buffer if entry[1]'s fence hasn't signaled yet. nsecs_t desiredPresent = bufferItem.mTimestamp; // desiredPresent比expectedPresent小了1 second多,或desiredPresent大於expectedPresent 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()); // 處理要drop的buffer if (!front->mIsStale) { // Front buffer is still in mSlots, so mark the slot as free // 對應的BufferSlot設定為FREE狀態 mSlots[front->mSlot].mBufferState.freeQueued(); // After leaving shared buffer mode, the shared buffer will // still be around. Mark it as no longer shared if this // operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[front->mSlot].mBufferState.isFree()) { mSlots[front->mSlot].mBufferState.mShared = false; } // mActiveBuffers :綁定了GraphicBuffer且狀態為非FREE的BufferSlot集合; // mFreeBuffers :綁定了GraphicBuffer且狀態為FREE的BufferSlot集合; // Don't put the shared buffer on the free list if (!mSlots[front->mSlot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(front->mSlot); // 從mActiveBuffers刪除 mCore->mFreeBuffers.push_back(front->mSlot);// 新增進mFreeBuffers } if (mCore->mBufferReleasedCbEnabled) { listener = mCore->mConnectedProducerListener; // 設定生產者的監聽器 } ++numDroppedBuffers; // 計數加1,記錄drop了幾個buffer } mCore->mQueue.erase(front);// 從mQueue中刪除 front = mCore->mQueue.begin();// 重置front,進入下一次while迴圈 } // See if the front buffer is ready to be acquired nsecs_t desiredPresent = front->mTimestamp; bool bufferIsDue = desiredPresent <= expectedPresent || desiredPresent > expectedPresent + MAX_REASONABLE_NSEC; bool consumerIsReady = maxFrameNumber > 0 ? front->mFrameNumber <= maxFrameNumber : true; if (!bufferIsDue || !consumerIsReady) { BQ_LOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64 " (%" PRId64 ") now=%" PRId64 " frame=%" PRIu64 " consumer=%" PRIu64, desiredPresent, expectedPresent, desiredPresent - expectedPresent, systemTime(CLOCK_MONOTONIC), front->mFrameNumber, maxFrameNumber); ATRACE_NAME("PRESENT_LATER"); return PRESENT_LATER; } BQ_LOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " " "(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent, desiredPresent - expectedPresent, systemTime(CLOCK_MONOTONIC)); } // 走到這裡就說明:該丟棄的已經都丟棄了,餘下的就可以拿去顯示了。 int slot = BufferQueueCore::INVALID_BUFFER_SLOT; if (sharedBufferAvailable && mCore->mQueue.empty()) { // make sure the buffer has finished allocating before acquiring it // 共享Buffer模式下處理 mCore->waitWhileAllocatingLocked(lock); 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 if (acquireNonDroppableBuffer && front->mIsDroppable) { BQ_LOGV("acquireBuffer: front buffer is not droppable"); return NO_BUFFER_AVAILABLE; } else { // 從front獲取對應的slot index slot = front->mSlot; *outBuffer = *front; } ATRACE_BUFFER_INDEX(slot); BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }", slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle); if (!outBuffer->mIsStale) { mSlots[slot].mAcquireCalled = true; // Don't decrease the queue count if the BufferItem wasn't // previously in the queue. This happens in shared buffer mode when // the queue is empty and the BufferItem is created above. if (mCore->mQueue.empty()) { mSlots[slot].mBufferState.acquireNotInQueue(); } else { // 將BufferState狀態改為acquire mSlots[slot].mBufferState.acquire(); } mSlots[slot].mFence = Fence::NO_FENCE; } // If the buffer has previously been acquired by the consumer, set // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer // on the consumer side if (outBuffer->mAcquireCalled) { outBuffer->mGraphicBuffer = nullptr; } //將該Buffer從mQueue中移除 mCore->mQueue.erase(front); // We might have freed a slot while dropping old buffers, or the producer // may be blocked waiting for the number of buffers in the queue to // decrease. mCore->mDequeueCondition.notify_all(); ATRACE_INT(mCore->mConsumerName.string(), static_cast<int32_t>(mCore->mQueue.size())); #ifndef NO_BINDER mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); #endif VALIDATE_CONSISTENCY(); } // 回撥,通知生產者 if (listener != nullptr) { for (int i = 0; i < numDroppedBuffers; ++i) { listener->onBufferReleased(); } } return NO_ERROR; }
acquireBuffer 函式中的邏輯也非常的清晰,原始碼中也做了詳細註釋。
主要就是這幾件事情:
- 判斷 BufferQueueCore 中的 mQueue 是否為空,mQueue 就是前面 BufferQueueProducer 呼叫 queueBuffer 函式時,將緩衝區入隊的容器;
- 取出對應的 BufferSlot(會有一些判斷規則,捨棄一些buffer);
- 將 BufferState 改為 acquire 狀態;
- 將該 Buffer 從 mQueue 中移除;
2.3 消費者acquire拿到buffer後又是怎樣通知release buffer呢?
要回答這個問題,我們需要在回到呼叫acquireBuffer的地方,即 BLASTBufferQueue::processNextBufferLocked 函式中,先看其程式碼:
void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
......
SurfaceComposerClient::Transaction localTransaction;
bool applyTransaction = true;
SurfaceComposerClient::Transaction* t = &localTransaction;
// acquireBuffer獲取要處理的buffer
BufferItem bufferItem;
status_t status =
mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
......
// 拿到了實際的GraphicBuffer了
auto buffer = bufferItem.mGraphicBuffer;
mNumFrameAvailable--;
// 某些情況下,直接releaseBuffer而無需送SurfaceFlinger合成顯示
mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
ReleaseCallbackId releaseCallbackId(buffer->getId(), mLastAcquiredFrameNumber);
mSubmitted[releaseCallbackId] = bufferItem;
....
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
// release buffer的回到函式
auto releaseBufferCallback =
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4);
t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback);
......
if (applyTransaction) {
t->setApplyToken(mApplyToken).apply();
}
}
上述程式碼,做了比較多的簡化,只保留我認為比較重要的部分。
- 呼叫acquireBuffer獲取一個BufferItem;
- 取出GraphicBuffer -- auto buffer = bufferItem.mGraphicBuffer;
- 通過事務Transaction來向SurfaceFlinger提交Buffer與圖層的屬性;
t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback);
對於setBuffer,就是設定傳遞給SF的buffer,並指定了一個releaseBufferCallback,暫時可以理解為SF消費完這個buffer,就會通過這個callback通知來釋放這個buffer。
本文作者@二的次方 2022-03-23 釋出於部落格園
在acquireBuffer中加入log,列印呼叫堆疊資訊,如下:
11-13 01:23:59.275 3016 3030 E BufferQueueConsumer: stackdump:#00 pc 000580ff /system/lib/libgui.so (android::BufferQueueConsumer::releaseBuffer(int, unsigned long long, android::sp<android::Fence> const&, void*, void*)+130)
11-13 01:23:59.275 3016 3030 E BufferQueueConsumer: stackdump:#01 pc 00059117 /system/lib/libgui.so (android::BufferQueueConsumer::releaseBuffer(int, unsigned long long, void*, void*, android::sp<android::Fence> const&)+30)
11-13 01:23:59.275 3016 3030 E BufferQueueConsumer: stackdump:#02 pc 00076d27 /system/lib/libgui.so (android::ConsumerBase::releaseBufferLocked(int, android::sp<android::GraphicBuffer>, void*, void*)+134)
11-13 01:23:59.275 3016 3030 E BufferQueueConsumer: stackdump:#03 pc 0007580d /system/lib/libgui.so (android::BufferItemConsumer::releaseBuffer(android::BufferItem const&, android::sp<android::Fence> const&)+140)
11-13 01:23:59.275 3016 3030 E BufferQueueConsumer: stackdump:#04 pc 0006c467 /system/lib/libgui.so (android::BLASTBufferQueue::releaseBufferCallback(android::ReleaseCallbackId const&, android::sp<android::Fence> const&, unsigned int, unsigned int)+1362)
11-13 01:23:59.275 3016 3030 E BufferQueueConsumer: stackdump:#05 pc 0006d827 /system/lib/libgui.so (android::releaseBufferCallbackThunk(android::wp<android::BLASTBufferQueue>, android::ReleaseCallbackId const&, android::sp<android::Fence> const&, unsigned int, unsigned int)+62)
11-13 01:23:59.276 3016 3030 E BufferQueueConsumer: stackdump:#06 pc 0007039b /system/lib/libgui.so (std::__1::__function::__func<std::__1::__bind<void (&)(android::wp<android::BLASTBufferQueue>, android::ReleaseCallbackId const&, android::sp<android::Fence> const&, unsigned int, unsigned int), android::wp<android::BLASTBufferQueue>, std::__1::placeholders::__ph<1> const&, std::__1::placeholders::__ph<2> const&, std::__1::placeholders::__ph<3> const&, std::__1::placeholders::__ph<4> const&>, std::__1::allocator<std::__1::__bind<void (&)(android::wp<android::BLASTBufferQueue>, android::ReleaseCallbackId const&, android::sp<android::Fence> const&, unsigned int, unsigned int), android::wp<android::BLASTBufferQueue>, std::__1::placeholders::__ph<1> const&, std::__1::placeholders::__ph<2> const&, std::__1::placeholders::__ph<3> const&, std::__1::placeholders::__ph<4> const&> >, void (android::ReleaseCallbackId const&, android::sp<android::Fence> const&, unsigned int, unsigned int)>::operator()(android::ReleaseCallbackId const&, android::sp<android::Fence> const&,
11-13 01:23:59.276 3016 3030 E BufferQueueConsumer: stackdump:#07 pc 000a7d47 /system/lib/libgui.so (android::TransactionCompletedListener::onTransactionCompleted(android::ListenerStats)+3382)
11-13 01:23:59.276 3016 3030 E BufferQueueConsumer: stackdump:#08 pc 000925a5 /system/lib/libgui.so (int android::SafeBnInterface<android::ITransactionCompletedListener>::callLocalAsync<void (android::ITransactionCompletedListener::*)(android::ListenerStats)>(android::Parcel const&, android::Parcel*, void (android::ITransactionCompletedListener::*)(android::ListenerStats))+204)
11-13 01:23:59.276 3016 3030 E BufferQueueConsumer: stackdump:#09 pc 00028ddb /system/lib/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+162)
看上面的呼叫棧,是不是一目瞭然,從遙遠的Binder來的神祕資訊觸發了這一些列的事件:
>>> releaseBufferCallbackThunk
>>> BLASTBufferQueue::releaseBufferCallback
>>> BufferItemConsumer::releaseBuffer
>>> ConsumerBase::releaseBufferLocked
>>> BufferQueueConsumer::releaseBuffer
2.4 releaseBuffer的邏輯
老規矩,直接看程式碼,是不是很簡單啊!
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 == nullptr) {
BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
releaseFence.get());
return BAD_VALUE;
}
sp<IProducerListener> listener;
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer.
// Ignore this for the shared buffer where the frame number can easily
// get out of sync due to the buffer being queued and acquired at the
// same time.
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();//置為FREE狀態
// After leaving shared buffer mode, the shared buffer will
// still be around. Mark it as no longer shared if this
// operation causes it to be free.
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);// 從mActiveBuffers中刪除
mCore->mFreeBuffers.push_back(slot);//加入到mFreeBuffers中
}
if (mCore->mBufferReleasedCbEnabled) {
listener = mCore->mConnectedProducerListener; / 設定listener
}
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
// 喚醒等待的執行緒
mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
} // Autolock scope
// Call back without lock held
if (listener != nullptr) {
listener->onBufferReleased(); //通知producer
}
return NO_ERROR;
}
releaseBuffer方法的流程相對簡單:
-
slot就是需要釋放的BufferSlot的序號;
-
Buffer的FrameNumber變了,可能Buffer已經重新分配,這個是不用管;
-
只能釋放acquire狀態的buffer序號,釋放後是Buffer放會mFreeBuffers中;
-
releaseFence,從consumer那邊傳過來,producer可以dequeue mFreeBuffers中的buffer,但是隻有releaseFence發訊號出來後,consumer才真正用完,producer才可以寫;
-
最後通過listener通知producer。
2.5 ProducerListener是怎樣工作的?
在前面的講解中,有幾處都有出現 listener->onBufferReleased() ,意思是通知producer有buffer釋放了。這個listener是在哪裡設定的?onBufferReleased又做了哪些工作呢?接下來分析
定義:/frameworks/native/libs/gui/include/gui/IProducerListener.h
首先我們看到這張類圖:
1. 在BufferQueueCore中有成員 sp<IProducerListener> mConnectedProducerListener,它就是用來處理onBufferReleased事件的;
2. mConnectedProducerListener是在哪裡被設定的呢?答案是 BufferQueueProducer::connect;
3. 根據呼叫棧來追蹤:
11-13 01:20:19.388 2955 3013 E BufferQueueProducer: stackdump:#00 pc 0005e667 /system/lib/libgui.so (android::BufferQueueProducer::connect(android::sp<android::IProducerListener> const&, int, bool, android::IGraphicBufferProducer::QueueBufferOutput*)+1018)
11-13 01:20:19.388 2955 3013 E BufferQueueProducer: stackdump:#01 pc 0006ee41 /system/lib/libgui.so (android::BBQBufferQueueProducer::connect(android::sp<android::IProducerListener> const&, int, bool, android::IGraphicBufferProducer::QueueBufferOutput*)+176)
11-13 01:20:19.388 2955 3013 E BufferQueueProducer: stackdump:#02 pc 000a268b /system/lib/libgui.so (android::Surface::connect(int, android::sp<android::IProducerListener> const&, bool)+138)
11-13 01:20:19.388 2955 3013 E BufferQueueProducer: stackdump:#03 pc 0009dd61 /system/lib/libgui.so (android::Surface::hook_perform(ANativeWindow*, int, ...)+128)
4. 可以肯定,是執行 native_window_api_connect() 一路走下來的。沿著這條路,看看listener是哪裡產生的呢?
看起來是這個位置:
int Surface::connect(int api) {
static sp<IProducerListener> listener = new StubProducerListener();
return connect(api, listener);
}
5. 不過,奇怪的事情發生了,StubProducerListener看起來沒有任何操作,乖乖(這一點還是很奇怪的)
6. BLASTBufferQueue中還有使用AsyncProducerListener做一層封裝,實現非同步處理;
class StubProducerListener : public BnProducerListener {
public:
virtual ~StubProducerListener();
virtual void onBufferReleased() {}
virtual bool needsReleaseNotify() { return false; }
};
三、小結
BufferQueue的運作流程到這裡就算講完了。生產者做了什麼事情?消費者做了什麼事情?圖形快取是怎樣流轉的?狀態是怎樣變化的?在幾篇文章中基本上都有做了或簡單或詳細的介紹。通過BufferQueue的幾篇文章,幫助自己建立起基本的邏輯框架,為我們後續研究和分析問題奠定基礎。