BufferQueue
我們再來看一下我們的應用代碼,下面是繪制Buffer的代碼。我們這里只繪制了一次,但是在Andoroid的系統中,界面的不斷更新的,也就是說,這里的繪制是一個不斷循環的過程。
// 11. draw the ANativeWindow
for (int i = 0; i < numBufs + 1; i++) {
// 12. dequeue a buffer
int hwcFD= -1;
err = aNativeWindow->dequeueBuffer(aNativeWindow, &aNativeBuffer, &hwcFD);
if (err != NO_ERROR) {
ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
strerror(-err), -err);
break;
}
// 13. make sure really control the dequeued buffer
sp<Fence> hwcFence(new Fence(hwcFD));
int waitResult = hwcFence->waitForever("dequeueBuffer_EmptyNative");
if (waitResult != OK) {
ALOGE("dequeueBuffer_EmptyNative: Fence::wait returned an error: %d", waitResult);
break;
}
sp<GraphicBuffer> buf(GraphicBuffer::from(aNativeBuffer));
// 14. Fill the buffer with black
uint8_t *img = NULL;
err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
if (err != NO_ERROR) {
ALOGE("error pushing blank frames: lock failed: %s (%d)", strerror(-err), -err);
break;
}
//15. Draw the window, here we fill the window with black.
*img = 0;
err = buf->unlock();
if (err != NO_ERROR) {
ALOGE("error pushing blank frames: unlock failed: %s (%d)", strerror(-err), -err);
break;
}
// 16. queue the buffer to display
int gpuFD = -1;
err = aNativeWindow->queueBuffer(aNativeWindow, buf->getNativeBuffer(), gpuFD);
if (err != NO_ERROR) {
ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", strerror(-err), -err);
break;
}
aNativeBuffer = NULL;
}
抽象一下,就是:
while {
dequeueBuffer
lock
unlock
queueBuffer
}
這里的GraphicBuffer是隊列中的Buffer, 循環使用,顯示完了,又可以用來繪制新的顯示數據。
我們可以來看一下,我們跑測試應用時的顯示數據流:
應用繪制完成后,將數據交還給BufferQueue,Layer這邊從BufferQueue中獲取數據,進行合成顯示。
擴展到多個界面時,數據流圖如下:
這中間過程復雜,我們一個流程一個流程的看。
dequeueBuffer申請buffer繪制
應用要進程繪制,首先要申請一塊Buffer,我們這邊ANativeWindow通過dequeueBuffer從BufferQueue中獲取一塊Buffer。ANativeWindow的dequeueBuffer初始化為Surface的hook_dequeueBuffer方法。
int Surface::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd) {
Surface* c = getSelf(window);
return c->dequeueBuffer(buffer, fenceFd);
}
通過hook函數,調到Surface的dequeueBuffer方法,dequeueBuffer比較長,我們分階段來看:
1.deqeue準備
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
... ...
{
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 != NULL) {
*buffer = gbuf.get();
*fenceFd = -1;
return OK;
}
}
} // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer
在準備階段,主要是處理,前面的設置的參數需求,對Buffer大小的需求,格式和usage的需求。這過程是被鎖mMutex鎖住的。這里的mSharedBufferMode是一種特殊的模式,是上層應用請求的,專門給特殊的應用使用的,主要是VR應用。因為VR應用要求低延時,BufferQueue采用的交換用的Buffer多了,延遲增加。為了降低延遲,設計了這個共享buffer的模式,Producer和Consumer共用一個Buffer。應用繪制完成后,直接給到Consumer進行顯示。后續我們的講解將直接跳過這么這種模式。
2.實際dequeue階段
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);
mLastDequeueDuration = systemTime() - startTime;
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
"(%d, %d, %d, %#" PRIx64 ") failed: %d",
reqWidth, reqHeight, reqFormat, reqUsage, result);
return result;
}
if (buf < 0 || buf >= NUM_BUFFER_SLOTS) {
ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf);
android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
return FAILED_TRANSACTION;
}
dequeue是通過mGraphicBufferProducer來完成的。dequeueBuffer參數就是我們需要的大小的需求,格式和usage參數。dequeue回來的就是buf,并不是具體的Buffer,而是Buffer的序號。
Surface這邊的dequeueBuffer暫停,我們先看看GraphicBufferProducer的dequeue函數。GraphicBufferProducer的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) {
ATRACE_CALL();
{ // Autolock scope
Mutex::Autolock 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;
}
前置條件判斷
- mConsumerName, 消費者的名字,這個是從Layer那邊過來的,這個buffer是屬于哪個Layer,哪個窗口。
- mIsAbandoned,表示BufferQueue是否被丟棄,丟棄后BufferQueue就不能用了。
- mConnectedApi,標識這個BufferQueue連接到了哪個API,App connect到BufferQueue時設置的
繼續看
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) {
... ...
status_t returnFlags = NO_ERROR;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
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;
}
需求參數的處理,寬高,format,都是應用傳過來的。usage這里會跟Consumer的位或一下,最終是Producer和Consumer兩個的總和。如果正在申請Buffer,waitWhileAllocatingLocked,這邊會去block等待。
接下里,根據參數,找到一個可用的Buffer
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,
&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;
}
}
}
found是Buffer的序號,這里采用while循環的去等待可用的Buffer,如果有Free的Buffer,將Buffer從mSlots中獲取出來GraphicBuffer。如果獲取到的Buffer和我們需要的Buffer寬高,屬性等不滿足。而Producer又不允許分配buffer,我們就將它釋放掉,重新獲取一個。直到找到我們需要的Buffer。
我們來看found是從哪兒來的~這里面的函數都比較長,waitForFreeSlotThenRelock也不例外。waitForFreeSlotThenRelock中就是一個while循環。
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
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;
}
}
留意BufferQueueCore的這個幾個數組,前面我們已經見過的mSlots,這里又有一個mActiveBuffers。mSlots是總的;這里的mActiveBuffers是活躍的,不包含free的狀態的。
這里我們先找出來,有多少個buffer是已經處于dequeued狀態的dequeuedCount;多少個是處于acquired狀態的。dequeued狀態就是被應用拿去繪制去了,acquired狀態就是buffer被消費者拿去合成顯示去了。
什么情況下能找到可用的Buffer?
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
int* found) const {
... ...
// 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) {
BQ_LOGE("%s: attempting to exceed the max dequeued buffer count "
"(%d)", callerString, mCore->mMaxDequeuedBufferCount);
return INVALID_OPERATION;
}
超過最大可dequeue數mMaxDequeuedBufferCount時,不能再dequeue到Buffer。太貪心了,吃著碗里的,看著鍋中的。如果出現這個問題,應該是應用繪制的很慢,或者是buffer存在了泄露。
再來看下面的這種情況:
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
... ...
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 (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();
}
}
}
}
BufferQueueCore又出來一個隊列mQueue,mQueue是一個FIFO的隊列應用繪制完成后,queue到BufferQueue中,其實就是queue到這個隊列里面。
tooManyBuffers表示應用已經繪制完成了,但是一直沒有被消費,處于queued狀態的buffer超過了maxBufferCount數,這個時候不能再分配,如果分配就會造成內存緊張。
我們這里的caller是Dequeue,getFreeBufferLocked和getFreeSlotLocked又引出BufferQueueCore的兩個隊列。mFreeBuffers和mFreeSlots。我們說過,這里的隊列是Buffer的序號,mFreeBuffers表示Buffer是Free的,這個序號對應的Buffer已經被分配出來了,只是現在沒有被使用。而mFreeSlots表示,序號是Free的,這些序號還沒有被用過,說明對應的是沒有Buffer,Buffer還沒有分配。
如果找不到,found還是為INVALID_BUFFER_SLOT。沒有關系,如果是tooManyBuffers太多,或是INVALID_BUFFER_SLOT,將再試一次tryAgain。
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) {
status_t result = mCore->mDequeueCondition.waitRelative(
mCore->mMutex, mDequeueTimeout);
if (result == TIMED_OUT) {
return result;
}
} else {
mCore->mDequeueCondition.wait(mCore->mMutex);
}
}
} // while (tryAgain)
return NO_ERROR;
}
tryAgain時,先看看dequeue Buffer是不是阻塞式的,如果不是,直接返回了,此時沒有dequeue到我們需要的buffer。如果是阻塞式的,就等著吧,等有Buffer release。等有兩種方式,一種是等固定的時間,一種是等mCore->mMutex。
當然,如果找到了可以用的Buffer,就不用tryAgain了,直接返回去了。
繼續來看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) {
... ...
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 == 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;
}
根據找到buffer序號,找到GraphicBuffer,再看看需不需要重新分配。SharedBufer不能重新分配buffer,直接返回了。如果不是共享buffer,將我們找到found加如mActiveBuffers隊列中。outSlot的buffer就是found。
如果需要重新分配,那就要釋放掉原來的Buffer。mSlots中需要的信息復位。returnFlags加上BUFFER_NEEDS_REALLOCATION。如果不需要重新分配,mCore->mBufferAge +1。
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
eglDisplay用以創建EGLSyncKHR。eglFence同步Buffer,上一個使用者使用完成后,將signal出來。outFence的值就是eglFence,共享buffer沒有fence。如果是共享的buffer,將found保存下來,以后就一直用這個 buffer了。
重新分配Buffer,需要重新new一個GraphicBuffer。
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
Mutex::Autolock lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
}
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
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
}
新分配的Buffer保存到mSlots[*outSlot].mGraphicBuffer。這里的mSlot是BufferQueueCore的mSlots的引用(看構造函數)。如果Buffer分配失敗了,Buffer的序號,放入隊列mFreeSlots中。
怎么分Buffer的后續再介紹,繼續看dequeueBuffer。
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;
}
attachedByConsumer,如果這個Buffer是Consumer這邊attach上來的,需要給到這個標識BUFFER_NEEDS_REALLOCATION給Producer,但是不需要去new已給,因為已經new過了。
eglClientWaitSyncKHR,等eglfence。這個邏輯現在已經很少走到了。
此外,返回的是returnFlags。
BufferQueueProducer的dequeueBuffer完了,讓我們回到Surface的dequeueBuffer。
3.dequeue后的處理階段
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
... ...
Mutex::Autolock lock(mMutex);
// Write this while holding the mutex
mLastDequeueStartTime = startTime;
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
if (enableFrameTimestamps) {
mFrameEventHistory->applyDelta(frameTimestamps);
}
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;
}
}
if (fence->isValid()) {
*fenceFd = fence->dup();
if (*fenceFd == -1) {
ALOGE("dequeueBuffer: error duping fence: %d", errno);
// dup() should never fail; something is badly wrong. Soldier on
// and hope for the best; the worst that should happen is some
// visible corruption that lasts until the next frame.
}
} else {
*fenceFd = -1;
}
*buffer = gbuf.get();
if (mSharedBufferMode && mAutoRefresh) {
mSharedBufferSlot = buf;
mSharedBufferHasBeenQueued = false;
} else if (mSharedBufferSlot == buf) {
mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
mSharedBufferHasBeenQueued = false;
}
return OK;
}
- 拿到Buffer后,首先是timestamp的處理,記錄一下dequeue的時間。
- 從Surface的mSlots中根據buffer序號,取出GraphicsBuffer gbuf。如果gbuf沒有,或者需要重新分配,再次通過BufferQueuerProducer的requestBuffer來完成。
- 最后是fenceFd的獲取,根據Buffer的Fence,dup獲得。
前面BufferQueuerProducer去dequeueBuffer時,只拿回了buffer的序號,并沒有GraphicBuffer過來。GraphicBuffer是通過這里的requestBuffer去獲取到的。獲取到后就直接保存在Surface的mSlots中,后續就不用再去request了。需要主要的是,這里并不是拷貝GraphicBuffer的內容,BufferQueue 是不會復制Buffer內容的;采用的是共享Buffer,Buffer基本都是通過句柄handle進行傳遞。
我們來看看requestBuffer~
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
BQ_LOGV("requestBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
... ...
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
BufferQueueProducer的requestBuffer挺簡單,直接根據buffer的序號,返回BufferQueueCore mSlots中對應的GraphicBuffer。
requestBuffer需要傳一個GraphicBuffer,這就比較大了,現在的顯示屏分辨率都很好,一個Buffer就幾兆了,這就是為什么dequeue時不直接傳Buffer的原因。
requestBuffer的binder邏輯,值得一看~
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
public:
explicit BpGraphicBufferProducer(const sp<IBinder>& impl)
: BpInterface<IGraphicBufferProducer>(impl)
{
}
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) {
*buf = new GraphicBuffer();
result = reply.read(**buf);
if(result != NO_ERROR) {
(*buf).clear();
return result;
}
}
result = reply.readInt32();
return result;
}
Bp端通過REQUEST_BUFFER transact到Bn端,Bp端new一個GraphicBuffer,再將Bn端的GraphicBuffer 讀過來,構成Bp端的Bufer。到達到這個目的,GraphicBuffer需要繼承Flattenable,能夠將GraphicBuffer序列化和反序列化,以實現Binder的傳輸。
status_t BnGraphicBufferProducer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case REQUEST_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != 0);
if (buffer != 0) {
reply->write(*buffer);
}
reply->writeInt32(result);
return NO_ERROR;
}
Bn端將Buffer寫到reply中,Bp端沖reply中讀。
到此,dequeueBuffer流程完了,我們來看看dequeue的流程圖:
queueBuffer處理
App拿到Buffer后,將往Buffer里面繪制各自的數據,我們的測試應用中,繪制都非常簡單。這里就不看了。我們來看繪制完成后,繪制的數據是怎么送去合成顯示的。
queueBuffer我們直接從Surface的queueBuffer開始看,ANativeWindow前面的流程都類似。
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("Surface::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
bool isAutoTimestamp = false;
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.0);
} else {
timestamp = mTimestamp;
}
int i = getSlotFromBufferLocked(buffer);
... ...
getSlotFromBufferLocked,dequeue時,根據buffer序號取Buffer;queue時,是根據Buffer去找序號,根據Buffer的handle去找的。
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
... ...
// Make sure the crop rectangle is entirely inside the buffer.
Rect crop(Rect::EMPTY_RECT);
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
fence, mStickyTransform, mEnableFrameTimestamps);
if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
input.setSurfaceDamage(Region::INVALID_REGION);
} else {
int width = buffer->width;
int height = buffer->height;
bool rotated90 = (mTransform ^ mStickyTransform) &
NATIVE_WINDOW_TRANSFORM_ROT_90;
if (rotated90) {
std::swap(width, height);
}
Region flippedRegion;
for (auto rect : mDirtyRegion) {
int left = rect.left;
int right = rect.right;
int top = height - rect.bottom; // Flip from OpenGL convention
int bottom = height - rect.top; // Flip from OpenGL convention
switch (mTransform ^ mStickyTransform) {
case NATIVE_WINDOW_TRANSFORM_ROT_90: {
// Rotate 270 degrees
Rect flippedRect{top, width - right, bottom, width - left};
flippedRegion.orSelf(flippedRect);
break;
}
case NATIVE_WINDOW_TRANSFORM_ROT_180: {
// Rotate 180 degrees
Rect flippedRect{width - right, height - bottom,
width - left, height - top};
flippedRegion.orSelf(flippedRect);
break;
}
case NATIVE_WINDOW_TRANSFORM_ROT_270: {
// Rotate 90 degrees
Rect flippedRect{height - bottom, left,
height - top, right};
flippedRegion.orSelf(flippedRect);
break;
}
default: {
Rect flippedRect{left, top, right, bottom};
flippedRegion.orSelf(flippedRect);
break;
}
}
}
input.setSurfaceDamage(flippedRegion);
}
- mCrop,用以剪切Buffer的,mCrop不能超過buffer的大小~也就是說,我們的buffer可以只顯示一部分。
- queueBuffer封裝了兩個對象QueueBufferInput和QueueBufferOutput,一個是queueBuffer時的輸入,已給是返回值。
- flippedRegion,Opengl里面采用是坐標系是左下為遠點,而Graphic&Display子系統中采用左上為遠點,所以這里需要做一下倒轉。另外,受transform的影響,這里也需要統一一下。
- SurfaceDamage,受損區域,表示Surface也就是Buffer的那些個區域被更新了。支持部分更新。
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
... ...
nsecs_t now = systemTime();
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
mLastQueueDuration = systemTime() - now;
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
if (mEnableFrameTimestamps) {
mFrameEventHistory->applyDelta(output.frameTimestamps);
mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
std::make_shared<FenceTime>(std::move(fence)));
mFrameEventHistory->updateSignalTimes();
}
mLastFrameNumber = mNextFrameNumber;
mDefaultWidth = output.width;
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
... ...
return err;
}
queueBuffer的實現也是在GraphicBufferProducer中完成的。queue完成后,會更新一些默認的數據mDefaultWidth和mDefaultHeight。mNextFrameNumber是Frame的number,以及timestamp。
BufferQueueProducer的queueBuffer也是一個幾百行的函數~
QueueBufferInput其實就是對Buffer的描述的封裝,通過QueueBufferInput能在Binder中進行傳輸。因此在queueBuffer函數中,現將QueueBufferInput deflate出來。
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
int64_t requestedPresentTimestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
Rect crop(Rect::EMPTY_RECT);
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
sp<Fence> acquireFence;
bool getFrameTimestamps = false;
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
if (acquireFence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
return BAD_VALUE;
}
auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
switch (scalingMode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
break;
default:
BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode);
return BAD_VALUE;
}
- requestedPresentTimestamp分兩中情況,一種是自動的,另外一種是應用控制的。如果是自動的,那就是queueBuffer時是時間
timestamp = systemTime(SYSTEM_TIME_MONOTONIC
。 - android_dataspace是數據空間,新增加的特性~
- fence可以是NO_FENCE,但是不能是空指針
- scalingmode,Video播放,或者camera預覽的時候用的比較多。當顯示的內容和屏幕的大小不成比例時,采用什么處理方式。SCALE_TO_WINDOW就是根據window的大小,縮放buffer,buffer的內容能被顯示全;SCALE_CROP,根據窗口大小,截取buffer,部分buffer的內容就不能顯示出來。
繼續往下看:
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
... ...
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
... ...//判斷buffer的有效性
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
Rect croppedRect(Rect::EMPTY_RECT);
crop.intersect(bufferRect, &croppedRect);
if (croppedRect != crop) {
BQ_LOGE("queueBuffer: crop rect is not contained within the "
"buffer in slot %d", slot);
return BAD_VALUE;
}
// Override UNKNOWN dataspace with consumer default
if (dataSpace == HAL_DATASPACE_UNKNOWN) {
dataSpace = mCore->mDefaultBufferDataSpace;
}
mSlots[slot].mFence = acquireFence;
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;
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;
// Cache the shared buffer data so that the BufferItem can be recreated.
if (mCore->mSharedBufferMode) {
mCore->mSharedBufferCache.crop = crop;
mCore->mSharedBufferCache.transform = transform;
mCore->mSharedBufferCache.scalingMode = static_cast<uint32_t>(
scalingMode);
mCore->mSharedBufferCache.dataspace = dataSpace;
}
output->bufferReplaced = false;
if (mCore->mQueue.empty()) {
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
// and simply queue this buffer
mCore->mQueue.push_back(item);
frameAvailableListener = mCore->mConsumerListener;
} else {
// When the queue is not empty, we need to look at the last buffer
// in the queue to see if we need to replace it
const BufferItem& last = mCore->mQueue.itemAt(
mCore->mQueue.size() - 1);
if (last.mIsDroppable) {
if (!last.mIsStale) {
... ...
}
// 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;
}
}
mCore->mBufferHasBeenQueued = true;
mCore->mDequeueCondition.broadcast();
mCore->mLastQueuedSlot = slot;
output->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
output->transformHint = mCore->mTransformHint;
output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
VALIDATE_CONSISTENCY();
} // Autolock scope
- 根據序號將GraphicBuffer取出來,不要懷疑,應用使用的graphicBuffer也是從mSlots中獲取過去的。
- Bufferqueue中,用BufferItem來描述buffer,GraphicBuffer以及描述,都封裝在BuferItem中。
- 封裝好的BufferItem,push到mQueue中
- Buffer 好了,可以消費了,Listener可以工作了, frameAvailableListener
- Occupancy,用來告訴內存統計,這里占用的內存大小
這里沒有太復雜的邏輯,關鍵是要理解這些屬性所表示的實際意義
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
... ...
int connectedApi;
sp<Fence> lastQueuedFence;
{ // scope for the lock
Mutex::Autolock lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
mCallbackCondition.wait(mCallbackMutex);
}
if (frameAvailableListener != NULL) {
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != NULL) {
frameReplacedListener->onFrameReplaced(item);
}
connectedApi = mCore->mConnectedApi;
lastQueuedFence = std::move(mLastQueueBufferFence);
mLastQueueBufferFence = std::move(acquireFence);
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
++mCurrentCallbackTicket;
mCallbackCondition.broadcast();
}
// Wait without lock held
if (connectedApi == NATIVE_WINDOW_API_EGL) {
// Waiting here allows for two full buffers to be queued but not a
// third. In the event that frames take varying time, this makes a
// small trade-off in favor of latency rather than throughput.
lastQueuedFence->waitForever("Throttling EGL Production");
}
// Update and get FrameEventHistory.
nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
NewFrameEventsEntry newFrameEventsEntry = {
currentFrameNumber,
postedTime,
requestedPresentTimestamp,
std::move(acquireFenceTime)
};
addAndGetFrameTimestamps(&newFrameEventsEntry,
getFrameTimestamps ? &output->frameTimestamps : nullptr);
return NO_ERROR;
}
- frameAvailableListener,通知消費者,Buffer可以消費了。記住這里,我們后續消費Buffer的流程從這里開始看。
- lastQueuedFence,上一幀queue的Buffer的Fence
- lastQueuedFence->waitForever,這里可能會比較耗時。Android在8.0及以后的版本,對fence的管理加強了,如果HAL實現的不好,這里會等很長時間。這個lastQueuedFence是上一針的acquireFence,acquirefence是繪制,一般是GPU那邊signal的,表示繪制已經完成。如果上一幀的fence一直沒有signal,說明上一幀一直沒有繪制完成,等在這里也是有道理的。當然有些芯片商的實現不太好,可能沒有完全理解Android的設計,實現的時候難免會造成不必要的block。
queueBuffer完成,相比dequeueBuffer,邏輯簡單一些,也就是將數據傳過來,封裝成BufferItem,push到BufferQueueCore的mQueue中,再通過frameAvailableListener通知消費者去消費。創建Layer時,我們看過,frameAvailableListener是Consumer那邊設置過來的。