android Gui系統之SurfaceFlinger(2)---BufferQueue
6 BufferQueue
上一篇已經說到,BufferQueue是SurfaceFlinger管理和消費surface的中介,我們就開始分析bufferqueue。
每個應用 可以由幾個BufferQueue?
應用繪製UI 所需的記憶體從何而來?
應用和SurfaceFlinger 如何互斥共享資源的訪問?
6.1 Buffer的狀態
const char* BufferSlot::bufferStateName(BufferState state) { switch (state) { case BufferSlot::DEQUEUED: return "DEQUEUED"; case BufferSlot::QUEUED: return "QUEUED"; case BufferSlot::FREE: return "FREE"; case BufferSlot::ACQUIRED: return "ACQUIRED"; default: return "Unknown"; } }
狀態變遷如下:FREE->DEQUEUED->QUEUED->ACQUIRED->FREE
BufferQueue的狀態遷移圖:
我們先來看,producer & comsumer 分別是什麼?
應用程式需要重新整理UI,所以就會產生surface到BufferQueue,so,producer 可以認為就是應用程式,也可以是上一篇裡面介紹的ISurfaceComposerClient。
comsumer不用看也知道,就是SurfaceFlinger。所以可以明確一個大致的流程就是,
1)應用需要重新整理UI,獲取一個buffer的緩衝區,這個操作就是dequeue。經過dequeue以後,該buffer被producer鎖定,其他Owner就不能插手了。
2)然後把surface寫入到該buffer裡面,當producer認為寫入結束後,就執行queue的操作,把buffer 歸還給bufferqueue。Owner也變成為bufferqueue。
3)當一段buffer裡面由資料以後,comsumer就會收到訊息,然後去獲取該buffer。
void BufferQueue::ProxyConsumerListener::onFrameAvailable( const android::BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); if (listener != NULL) { listener->onFrameAvailable(item); } }
bufferqueue裡面就是comsumerlistener,當有可以使用的buffer後,就會通知comsumer使用。
// mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. sp<GraphicBuffer> mGraphicBuffer;
可以看到註釋,bufferqueue的mSlot[64],並不是都有內容的,也就是mSlot存的是buferr的指標,如果沒有,就存null
slot的個數在andorid5.0 裡面定義在BufferQueueDefs.h裡面,
enum { NUM_BUFFER_SLOTS = 64 };
6.2 Buffer記憶體的出處
既然producer是主動操作,所以如果在dequeue的時候,已經獲取了記憶體,後面的操作也就不需要分配記憶體了。
status_t BufferQueueProducer::dequeueBuffer(int *outSlot, sp<android::Fence> *outFence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { ATRACE_CALL(); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; } // Autolock scope BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x", async ? "true" : "false", 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 Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); if (format == 0) { format = mCore->mDefaultBufferFormat; } // Enable the usage bits the consumer requested usage |= mCore->mConsumerUsageBits; int found; status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async, &found, &returnFlags); 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; } *outSlot = found; ATRACE_BUFFER_INDEX(found); attachedByConsumer = mSlots[found].mAttachedByConsumer; const bool useDefaultSize = !width && !height; if (useDefaultSize) { width = mCore->mDefaultWidth; height = mCore->mDefaultHeight; } mSlots[found].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); if ((buffer == NULL) || (static_cast<uint32_t>(buffer->width) != width) || (static_cast<uint32_t>(buffer->height) != height) || (static_cast<uint32_t>(buffer->format) != format) || ((static_cast<uint32_t>(buffer->usage) & usage) != 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; returnFlags |= BUFFER_NEEDS_REALLOCATION; } if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { 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; *outFence = mSlots[found].mFence; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { status_t error; BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( width, height, format, usage, &error)); if (graphicBuffer == NULL) { BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); return error; } { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } mSlots[*outSlot].mFrameNumber = UINT32_MAX; mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } // 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); return returnFlags; }
step1:BufferQueueProducer::waitForFreeSlotThenRelock 迴圈的主要作用就是查詢可以使用的slot。
step2:釋放不需要的buffer,並且統計已分配的記憶體。
// Free up any buffers that are in slots beyond the max buffer count for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { assert(mSlots[s].mBufferState == BufferSlot::FREE); if (mSlots[s].mGraphicBuffer != NULL) { mCore->freeBufferLocked(s); *returnFlags |= RELEASE_ALL_BUFFERS; } }
for (int s = 0; s < maxBufferCount; ++s) { switch (mSlots[s].mBufferState) { case BufferSlot::DEQUEUED: ++dequeuedCount; break; case BufferSlot::ACQUIRED: ++acquiredCount; break; case BufferSlot::FREE: // We return the oldest of the free buffers to avoid // stalling the producer if possible, since the consumer // may still have pending reads of in-flight buffers if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) { *found = s; } break; default: break; } }
如果有合適的,found 就是可以使用的buffer編號。
如果dequeue too many,but comsumer還來不及消耗掉,這個時候,有可能會導致OOM,所以,判斷是否在佇列裡面有過多的buffer。
等待comsumer消耗後,釋放互斥鎖。
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 && (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { return WOULD_BLOCK; } mCore->mDequeueCondition.wait(mCore->mMutex); }
在返回dqueueBuffer這個方法:如果沒有找到free的slot,就直接返回錯誤。當然正常情況下是不會發生的。
// This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { BQ_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; }
mSlots[found].mBufferState = BufferSlot::DEQUEUED;
把找到的buffer的狀態設為DEQUEUE。
在判斷了mSlot[found]的屬性以後,它可能是空的,也有可能不符合當前需要的buffer的size,就給mSlot[found]分配新的屬性和記憶體
if ((buffer == NULL) || (static_cast<uint32_t>(buffer->width) != width) || (static_cast<uint32_t>(buffer->height) != height) || (static_cast<uint32_t>(buffer->format) != format) || ((static_cast<uint32_t>(buffer->usage) & usage) != 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; returnFlags |= BUFFER_NEEDS_REALLOCATION; } if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { 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; *outFence = mSlots[found].mFence; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { status_t error; BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( width, height, format, usage, &error)); if (graphicBuffer == NULL) { BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); return error; } { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } mSlots[*outSlot].mFrameNumber = UINT32_MAX; mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } // Autolock scope }
這樣buffer對應的記憶體就是在producer,dequeue操作的時候分配記憶體的。(if need)
6.3應用程式和BufferQueue的關係
首先看一張surface各類之間的關係:
這裡多了一個Layer的東西,Layer代表一個畫面的圖層(在android app的角度以前是沒有圖層的概念的,雖然framework有)。
SurfaceFlinger混合,就是把所有的layer做混合。
我們先來看createlayer:
status_t SurfaceFlinger::createLayer( const String8& name, const sp<Client>& client, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
這裡關鍵是handler & gbp這2個引數。
我們看下去程式碼:layer 是從createNormalLayer裡面來的
status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) { // initialize the surfaces switch (format) { case PIXEL_FORMAT_TRANSPARENT: case PIXEL_FORMAT_TRANSLUCENT: format = PIXEL_FORMAT_RGBA_8888; break; case PIXEL_FORMAT_OPAQUE: format = PIXEL_FORMAT_RGBX_8888; break; } *outLayer = new Layer(this, client, name, w, h, flags); status_t err = (*outLayer)->setBuffers(w, h, format, flags); if (err == NO_ERROR) { *handle = (*outLayer)->getHandle(); *gbp = (*outLayer)->getProducer(); } ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err)); return err; }
producer跟蹤原始碼可以看到:
class MonitoredProducer : public IGraphicBufferProducer
通過bind機制,可以認為就是BufferQueue的一個子類。
所以,每一個bufferqueue對應的都是一個layer。
看下handler:
sp<IBinder> Layer::getHandle() { Mutex::Autolock _l(mLock); LOG_ALWAYS_FATAL_IF(mHasSurface, "Layer::getHandle() has already been called"); mHasSurface = true; /* * The layer handle is just a BBinder object passed to the client * (remote process) -- we don't keep any reference on our side such that * the dtor is called when the remote side let go of its reference. * * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for * this layer when the handle is destroyed. */ class Handle : public BBinder, public LayerCleaner { wp<const Layer> mOwner; public: Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) : LayerCleaner(flinger, layer), mOwner(layer) { } }; return new Handle(mFlinger, this); }
沒有什麼東西,就是LayerCleaner,
它的設計目的就是SurfaceFlinger來清除圖層。
所以我們可以得出結論
1)一個app對應一個surfaceFlinger,可以有多個layer,從而對應多個bufferqueue
2)surface的緩衝區記憶體是BufferQueue在進行dequeue的時候分配的,屬於client端。
3)App & SurfaceFlinger都通過bufferQueue來分配和使用緩衝區,所以互斥操作是由BufferQueue來實現。
參考:
《深入理解android核心設計思想》 林學森