/* * 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 <inttypes.h> #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(const sp<IGraphicBufferConsumer>& consumer, uint32_t consumerUsage, int bufferCount) : ConsumerBase(consumer), mBufferCount(bufferCount), mLatestTimestamp(0) { mConsumer->setConsumerUsageBits(consumerUsage); mConsumer->setMaxAcquiredBufferCount(bufferCount); assert(bufferCount > 0); } RingBufferConsumer::~RingBufferConsumer() { } void RingBufferConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; mConsumer->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; } nsecs_t RingBufferConsumer::getLatestTimestamp() { Mutex::Autolock _l(mMutex); if (mBufferItemList.size() == 0) { return 0; } return mLatestTimestamp; } 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 %" PRId64 ", framenumber %" PRIu64 ")", item.mTimestamp, item.mFrameNumber); } else { BI_LOGV("Pinned buffer (frame %" PRIu64 ", timestamp %" PRId64 ")", 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. // item.mGraphicBuffer was populated with the proper graphic-buffer // at acquire even if it was previously acquired err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, item.mFence); if (err != OK) { BI_LOGE("Failed to add release fence to buffer " "(timestamp %" PRId64 ", framenumber %" PRIu64, item.mTimestamp, item.mFrameNumber); return err; } BI_LOGV("Attempting to release buffer timestamp %" PRId64 ", frame %" PRIu64, item.mTimestamp, item.mFrameNumber); // item.mGraphicBuffer was populated with the proper graphic-buffer // at acquire even if it was previously acquired err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer, 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 %" PRId64 ", frame %" PRIu64 " 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(const BufferItem& item) { 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, 0); 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 %" PRId64 "), " "buffer items %zu out of %d", item.mTimestamp, mBufferItemList.size(), mBufferCount); if (item.mTimestamp < mLatestTimestamp) { BI_LOGE("Timestamp decreases from %" PRId64 " to %" PRId64, mLatestTimestamp, item.mTimestamp); } mLatestTimestamp = item.mTimestamp; item.mGraphicBuffer = mSlots[item.mBuf].mGraphicBuffer; } // end of mMutex lock ConsumerBase::onFrameAvailable(item); } 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.mGraphicBuffer, item.mFence); if (res != OK) { BI_LOGE("Failed to add release fence to buffer " "(timestamp %" PRId64 ", framenumber %" PRIu64, 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 %" PRId64 ", framenumber %" PRIu64 ")", item.mTimestamp, item.mFrameNumber); } else { BI_LOGV("Unpinned buffer (timestamp %" PRId64 ", framenumber %" PRIu64 ")", item.mTimestamp, item.mFrameNumber); } } status_t RingBufferConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { Mutex::Autolock _l(mMutex); return mConsumer->setDefaultBufferSize(w, h); } status_t RingBufferConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { Mutex::Autolock _l(mMutex); return mConsumer->setDefaultBufferFormat(defaultFormat); } status_t RingBufferConsumer::setConsumerUsage(uint32_t usage) { Mutex::Autolock _l(mMutex); return mConsumer->setConsumerUsageBits(usage); } } // namespace android