| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "RingBufferConsumer" |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| #include <utils/Log.h> |
| |
| #include <gui/RingBufferConsumer.h> |
| |
| #define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) |
| #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) |
| #define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) |
| #define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) |
| #define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) |
| |
| #undef assert |
| #define assert(x) ALOG_ASSERT((x), #x) |
| |
| typedef android::RingBufferConsumer::PinnedBufferItem PinnedBufferItem; |
| |
| namespace android { |
| |
| RingBufferConsumer::RingBufferConsumer(uint32_t consumerUsage, |
| int bufferCount) : |
| ConsumerBase(new BufferQueue(true)), |
| mBufferCount(bufferCount) |
| { |
| mBufferQueue->setConsumerUsageBits(consumerUsage); |
| mBufferQueue->setSynchronousMode(true); |
| mBufferQueue->setMaxAcquiredBufferCount(bufferCount); |
| |
| assert(bufferCount > 0); |
| } |
| |
| RingBufferConsumer::~RingBufferConsumer() { |
| } |
| |
| void RingBufferConsumer::setName(const String8& name) { |
| Mutex::Autolock _l(mMutex); |
| mName = name; |
| mBufferQueue->setConsumerName(name); |
| } |
| |
| sp<PinnedBufferItem> RingBufferConsumer::pinSelectedBuffer( |
| const RingBufferComparator& filter, |
| bool waitForFence) { |
| |
| sp<PinnedBufferItem> pinnedBuffer; |
| |
| { |
| List<RingBufferItem>::iterator it, end, accIt; |
| BufferInfo acc, cur; |
| BufferInfo* accPtr = NULL; |
| |
| Mutex::Autolock _l(mMutex); |
| |
| for (it = mBufferItemList.begin(), end = mBufferItemList.end(); |
| it != end; |
| ++it) { |
| |
| const RingBufferItem& item = *it; |
| |
| cur.mCrop = item.mCrop; |
| cur.mTransform = item.mTransform; |
| cur.mScalingMode = item.mScalingMode; |
| cur.mTimestamp = item.mTimestamp; |
| cur.mFrameNumber = item.mFrameNumber; |
| cur.mPinned = item.mPinCount > 0; |
| |
| int ret = filter.compare(accPtr, &cur); |
| |
| if (ret == 0) { |
| accPtr = NULL; |
| } else if (ret > 0) { |
| acc = cur; |
| accPtr = &acc; |
| accIt = it; |
| } // else acc = acc |
| } |
| |
| if (!accPtr) { |
| return NULL; |
| } |
| |
| pinnedBuffer = new PinnedBufferItem(this, *accIt); |
| pinBufferLocked(pinnedBuffer->getBufferItem()); |
| |
| } // end scope of mMutex autolock |
| |
| if (waitForFence) { |
| status_t err = pinnedBuffer->getBufferItem().mFence->waitForever( |
| "RingBufferConsumer::pinSelectedBuffer"); |
| if (err != OK) { |
| BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", |
| strerror(-err), err); |
| } |
| } |
| |
| return pinnedBuffer; |
| } |
| |
| status_t RingBufferConsumer::clear() { |
| |
| status_t err; |
| Mutex::Autolock _l(mMutex); |
| |
| BI_LOGV("%s", __FUNCTION__); |
| |
| // Avoid annoying log warnings by returning early |
| if (mBufferItemList.size() == 0) { |
| return OK; |
| } |
| |
| do { |
| size_t pinnedFrames = 0; |
| err = releaseOldestBufferLocked(&pinnedFrames); |
| |
| if (err == NO_BUFFER_AVAILABLE) { |
| assert(pinnedFrames == mBufferItemList.size()); |
| break; |
| } |
| |
| if (err == NOT_ENOUGH_DATA) { |
| // Fine. Empty buffer item list. |
| break; |
| } |
| |
| if (err != OK) { |
| BI_LOGE("Clear failed, could not release buffer"); |
| return err; |
| } |
| |
| } while(true); |
| |
| return OK; |
| } |
| |
| void RingBufferConsumer::pinBufferLocked(const BufferItem& item) { |
| List<RingBufferItem>::iterator it, end; |
| |
| for (it = mBufferItemList.begin(), end = mBufferItemList.end(); |
| it != end; |
| ++it) { |
| |
| RingBufferItem& find = *it; |
| if (item.mGraphicBuffer == find.mGraphicBuffer) { |
| find.mPinCount++; |
| break; |
| } |
| } |
| |
| if (it == end) { |
| BI_LOGE("Failed to pin buffer (timestamp %lld, framenumber %lld)", |
| item.mTimestamp, item.mFrameNumber); |
| } else { |
| BI_LOGV("Pinned buffer (frame %lld, timestamp %lld)", |
| item.mFrameNumber, item.mTimestamp); |
| } |
| } |
| |
| status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) { |
| status_t err = OK; |
| |
| List<RingBufferItem>::iterator it, end, accIt; |
| |
| it = mBufferItemList.begin(); |
| end = mBufferItemList.end(); |
| accIt = end; |
| |
| if (it == end) { |
| /** |
| * This is fine. We really care about being able to acquire a buffer |
| * successfully after this function completes, not about it releasing |
| * some buffer. |
| */ |
| BI_LOGV("%s: No buffers yet acquired, can't release anything", |
| __FUNCTION__); |
| return NOT_ENOUGH_DATA; |
| } |
| |
| for (; it != end; ++it) { |
| RingBufferItem& find = *it; |
| |
| if (find.mPinCount > 0) { |
| if (pinnedFrames != NULL) { |
| ++(*pinnedFrames); |
| } |
| // Filter out pinned frame when searching for buffer to release |
| continue; |
| } |
| |
| if (find.mTimestamp < accIt->mTimestamp || accIt == end) { |
| accIt = it; |
| } |
| } |
| |
| if (accIt != end) { |
| RingBufferItem& item = *accIt; |
| |
| // In case the object was never pinned, pass the acquire fence |
| // back to the release fence. If the fence was already waited on, |
| // it'll just be a no-op to wait on it again. |
| err = addReleaseFenceLocked(item.mBuf, item.mFence); |
| |
| if (err != OK) { |
| BI_LOGE("Failed to add release fence to buffer " |
| "(timestamp %lld, framenumber %lld", |
| item.mTimestamp, item.mFrameNumber); |
| return err; |
| } |
| |
| BI_LOGV("Attempting to release buffer timestamp %lld, frame %lld", |
| item.mTimestamp, item.mFrameNumber); |
| |
| err = releaseBufferLocked(item.mBuf, |
| EGL_NO_DISPLAY, |
| EGL_NO_SYNC_KHR); |
| if (err != OK) { |
| BI_LOGE("Failed to release buffer: %s (%d)", |
| strerror(-err), err); |
| return err; |
| } |
| |
| BI_LOGV("Buffer timestamp %lld, frame %lld evicted", |
| item.mTimestamp, item.mFrameNumber); |
| |
| size_t currentSize = mBufferItemList.size(); |
| mBufferItemList.erase(accIt); |
| assert(mBufferItemList.size() == currentSize - 1); |
| } else { |
| BI_LOGW("All buffers pinned, could not find any to release"); |
| return NO_BUFFER_AVAILABLE; |
| |
| } |
| |
| return OK; |
| } |
| |
| void RingBufferConsumer::onFrameAvailable() { |
| status_t err; |
| |
| { |
| Mutex::Autolock _l(mMutex); |
| |
| /** |
| * Release oldest frame |
| */ |
| if (mBufferItemList.size() >= (size_t)mBufferCount) { |
| err = releaseOldestBufferLocked(/*pinnedFrames*/NULL); |
| assert(err != NOT_ENOUGH_DATA); |
| |
| // TODO: implement the case for NO_BUFFER_AVAILABLE |
| assert(err != NO_BUFFER_AVAILABLE); |
| if (err != OK) { |
| return; |
| } |
| // TODO: in unpinBuffer rerun this routine if we had buffers |
| // we could've locked but didn't because there was no space |
| } |
| |
| RingBufferItem& item = *mBufferItemList.insert(mBufferItemList.end(), |
| RingBufferItem()); |
| |
| /** |
| * Acquire new frame |
| */ |
| err = acquireBufferLocked(&item); |
| if (err != OK) { |
| if (err != NO_BUFFER_AVAILABLE) { |
| BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); |
| } |
| |
| mBufferItemList.erase(--mBufferItemList.end()); |
| return; |
| } |
| |
| BI_LOGV("New buffer acquired (timestamp %lld), " |
| "buffer items %u out of %d", |
| item.mTimestamp, |
| mBufferItemList.size(), mBufferCount); |
| |
| item.mGraphicBuffer = mSlots[item.mBuf].mGraphicBuffer; |
| } // end of mMutex lock |
| |
| ConsumerBase::onFrameAvailable(); |
| } |
| |
| void RingBufferConsumer::unpinBuffer(const BufferItem& item) { |
| Mutex::Autolock _l(mMutex); |
| |
| List<RingBufferItem>::iterator it, end, accIt; |
| |
| for (it = mBufferItemList.begin(), end = mBufferItemList.end(); |
| it != end; |
| ++it) { |
| |
| RingBufferItem& find = *it; |
| if (item.mGraphicBuffer == find.mGraphicBuffer) { |
| status_t res = addReleaseFenceLocked(item.mBuf, item.mFence); |
| |
| if (res != OK) { |
| BI_LOGE("Failed to add release fence to buffer " |
| "(timestamp %lld, framenumber %lld", |
| item.mTimestamp, item.mFrameNumber); |
| return; |
| } |
| |
| find.mPinCount--; |
| break; |
| } |
| } |
| |
| if (it == end) { |
| // This should never happen. If it happens, we have a bug. |
| BI_LOGE("Failed to unpin buffer (timestamp %lld, framenumber %lld)", |
| item.mTimestamp, item.mFrameNumber); |
| } else { |
| BI_LOGV("Unpinned buffer (timestamp %lld, framenumber %lld)", |
| item.mTimestamp, item.mFrameNumber); |
| } |
| } |
| |
| status_t RingBufferConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { |
| Mutex::Autolock _l(mMutex); |
| return mBufferQueue->setDefaultBufferSize(w, h); |
| } |
| |
| status_t RingBufferConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { |
| Mutex::Autolock _l(mMutex); |
| return mBufferQueue->setDefaultBufferFormat(defaultFormat); |
| } |
| |
| status_t RingBufferConsumer::setConsumerUsage(uint32_t usage) { |
| Mutex::Autolock _l(mMutex); |
| return mBufferQueue->setConsumerUsageBits(usage); |
| } |
| |
| } // namespace android |