/*
* 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