Commit 789a0c82 authored by Pablo Ceballos's avatar Pablo Ceballos
Browse files

Fix Surface slot caching

- Now that it's possible to resize a BufferQueue while buffers are
  dequeued/acquired, it's no longer correct for Surface to clear its
  cache when the BufferQueue is resized since it must keep at least
  the currently dequeued buffers.
- Add an onSlotsFreed callback to IProducerListener so that producers
  that wish to be notified about buffers being freed can do so. Note
  that this isn't currently used in Surface.
- Review and fixup all the places where the producer/consumer
  listeners for freed buffers should be called.

Change-Id: I4ab0c92bc69b75a3c072ddf5d74d78f941dba4c8
parent c5cec281
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <list> #include <list>
#include <set> #include <set>
#include <vector>
#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
...@@ -120,8 +121,9 @@ private: ...@@ -120,8 +121,9 @@ private:
void freeAllBuffersLocked(); void freeAllBuffersLocked();
// If delta is positive, makes more slots available. If negative, takes // If delta is positive, makes more slots available. If negative, takes
// away slots. Returns false if the request can't be met. // away slots. Returns false if the request can't be met. Any slots that
bool adjustAvailableSlotsLocked(int delta); // were freed will be appended to freedSlots.
bool adjustAvailableSlotsLocked(int delta, std::vector<int>* freedSlots);
// waitWhileAllocatingLocked blocks until mIsAllocating is false. // waitWhileAllocatingLocked blocks until mIsAllocating is false.
void waitWhileAllocatingLocked() const; void waitWhileAllocatingLocked() const;
......
...@@ -41,6 +41,9 @@ public: ...@@ -41,6 +41,9 @@ public:
// This is called without any lock held and can be called concurrently by // This is called without any lock held and can be called concurrently by
// multiple threads. // multiple threads.
virtual void onBufferReleased() = 0; // Asynchronous virtual void onBufferReleased() = 0; // Asynchronous
// onSlotFreed is called when the BufferQueue frees a buffer in a slot.
virtual void onSlotFreed(int /*slot*/) {}; // Asynchronous
}; };
class IProducerListener : public ProducerListener, public IInterface class IProducerListener : public ProducerListener, public IInterface
......
...@@ -274,35 +274,51 @@ status_t BufferQueueConsumer::detachBuffer(int slot) { ...@@ -274,35 +274,51 @@ status_t BufferQueueConsumer::detachBuffer(int slot) {
ATRACE_CALL(); ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot); ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot); BQ_LOGV("detachBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) { sp<IConsumerListener> consumerListener;
BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); sp<IProducerListener> producerListener;
return NO_INIT; {
} Mutex::Autolock lock(mCore->mMutex);
if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) { if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer" BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
"mode"); return NO_INIT;
return BAD_VALUE; }
if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
"mode");
return BAD_VALUE;
}
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isAcquired()) {
BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
"(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
}
mSlots[slot].mBufferState.detachConsumer();
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
mCore->mDequeueCondition.broadcast();
VALIDATE_CONSISTENCY();
producerListener = mCore->mConnectedProducerListener;
consumerListener = mCore->mConsumerListener;
} }
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { // Call back without lock held
BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", if (producerListener != NULL) {
slot, BufferQueueDefs::NUM_BUFFER_SLOTS); producerListener->onSlotFreed(slot);
return BAD_VALUE; }
} else if (!mSlots[slot].mBufferState.isAcquired()) { if (consumerListener != NULL) {
BQ_LOGE("detachBuffer: slot %d is not owned by the consumer " consumerListener->onBuffersReleased();
"(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
} }
mSlots[slot].mBufferState.detachConsumer();
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
mCore->mDequeueCondition.broadcast();
VALIDATE_CONSISTENCY();
return NO_ERROR; return NO_ERROR;
} }
...@@ -573,30 +589,40 @@ status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) { ...@@ -573,30 +589,40 @@ status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) {
return BAD_VALUE; return BAD_VALUE;
} }
Mutex::Autolock lock(mCore->mMutex); sp<IConsumerListener> listener;
{
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("setMaxBufferCount: producer is already connected");
return INVALID_OPERATION;
}
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { if (bufferCount < mCore->mMaxAcquiredBufferCount) {
BQ_LOGE("setMaxBufferCount: producer is already connected"); BQ_LOGE("setMaxBufferCount: invalid buffer count (%d) less than"
return INVALID_OPERATION; "mMaxAcquiredBufferCount (%d)", bufferCount,
} mCore->mMaxAcquiredBufferCount);
return BAD_VALUE;
}
if (bufferCount < mCore->mMaxAcquiredBufferCount) { int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
BQ_LOGE("setMaxBufferCount: invalid buffer count (%d) less than" mCore->mDequeueBufferCannotBlock, bufferCount) -
"mMaxAcquiredBufferCount (%d)", bufferCount, mCore->getMaxBufferCountLocked();
mCore->mMaxAcquiredBufferCount); if (!mCore->adjustAvailableSlotsLocked(delta, nullptr)) {
return BAD_VALUE; BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number"
} " of available slots. Delta = %d", delta);
return BAD_VALUE;
}
int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, mCore->mMaxBufferCount = bufferCount;
mCore->mDequeueBufferCannotBlock, bufferCount) - if (delta < 0) {
mCore->getMaxBufferCountLocked(); listener = mCore->mConsumerListener;
if (!mCore->adjustAvailableSlotsLocked(delta)) { }
BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number of "
"available slots. Delta = %d", delta);
return BAD_VALUE;
} }
mCore->mMaxBufferCount = bufferCount; // Call back without lock held
if (listener != NULL) {
listener->onBuffersReleased();
}
return NO_ERROR; return NO_ERROR;
} }
...@@ -611,7 +637,9 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( ...@@ -611,7 +637,9 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
return BAD_VALUE; return BAD_VALUE;
} }
sp<IConsumerListener> listener; sp<IConsumerListener> consumerListener;
sp<IProducerListener> producerListener;
std::vector<int> freedSlots;
{ // Autolock scope { // Autolock scope
Mutex::Autolock lock(mCore->mMutex); Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked(); mCore->waitWhileAllocatingLocked();
...@@ -648,7 +676,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( ...@@ -648,7 +676,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
} }
int delta = maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount; int delta = maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount;
if (!mCore->adjustAvailableSlotsLocked(delta)) { if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
return BAD_VALUE; return BAD_VALUE;
} }
...@@ -656,12 +684,19 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( ...@@ -656,12 +684,19 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
VALIDATE_CONSISTENCY(); VALIDATE_CONSISTENCY();
if (delta < 0) { if (delta < 0) {
listener = mCore->mConsumerListener; consumerListener = mCore->mConsumerListener;
producerListener = mCore->mConnectedProducerListener;
} }
} }
// Call back without lock held // Call back without lock held
if (listener != NULL) { if (consumerListener != NULL) {
listener->onBuffersReleased(); consumerListener->onBuffersReleased();
}
if (producerListener != NULL) {
for (int i : freedSlots) {
producerListener->onSlotFreed(i);
}
} }
return NO_ERROR; return NO_ERROR;
......
...@@ -225,7 +225,8 @@ void BufferQueueCore::freeAllBuffersLocked() { ...@@ -225,7 +225,8 @@ void BufferQueueCore::freeAllBuffersLocked() {
VALIDATE_CONSISTENCY(); VALIDATE_CONSISTENCY();
} }
bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) { bool BufferQueueCore::adjustAvailableSlotsLocked(int delta,
std::vector<int>* freedSlots) {
if (delta >= 0) { if (delta >= 0) {
// If we're going to fail, do so before modifying anything // If we're going to fail, do so before modifying anything
if (delta > static_cast<int>(mUnusedSlots.size())) { if (delta > static_cast<int>(mUnusedSlots.size())) {
...@@ -252,11 +253,17 @@ bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) { ...@@ -252,11 +253,17 @@ bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
clearBufferSlotLocked(*slot); clearBufferSlotLocked(*slot);
mUnusedSlots.push_back(*slot); mUnusedSlots.push_back(*slot);
mFreeSlots.erase(slot); mFreeSlots.erase(slot);
if (freedSlots) {
freedSlots->push_back(*slot);
}
} else if (!mFreeBuffers.empty()) { } else if (!mFreeBuffers.empty()) {
int slot = mFreeBuffers.back(); int slot = mFreeBuffers.back();
clearBufferSlotLocked(slot); clearBufferSlotLocked(slot);
mUnusedSlots.push_back(slot); mUnusedSlots.push_back(slot);
mFreeBuffers.pop_back(); mFreeBuffers.pop_back();
if (freedSlots) {
freedSlots->push_back(slot);
}
} else { } else {
return false; return false;
} }
......
...@@ -90,7 +90,9 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( ...@@ -90,7 +90,9 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount(
BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d", BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
maxDequeuedBuffers); maxDequeuedBuffers);
sp<IConsumerListener> listener; sp<IConsumerListener> consumerListener;
sp<IProducerListener> producerListener;
std::vector<int> freedSlots;
{ // Autolock scope { // Autolock scope
Mutex::Autolock lock(mCore->mMutex); Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked(); mCore->waitWhileAllocatingLocked();
...@@ -142,20 +144,26 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( ...@@ -142,20 +144,26 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount(
} }
int delta = maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount; int delta = maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount;
if (!mCore->adjustAvailableSlotsLocked(delta)) { if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
return BAD_VALUE; return BAD_VALUE;
} }
mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers; mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
VALIDATE_CONSISTENCY(); VALIDATE_CONSISTENCY();
if (delta < 0) { if (delta < 0) {
listener = mCore->mConsumerListener; consumerListener = mCore->mConsumerListener;
producerListener = mCore->mConnectedProducerListener;
} }
mCore->mDequeueCondition.broadcast(); mCore->mDequeueCondition.broadcast();
} // Autolock scope } // Autolock scope
// Call back without lock held // Call back without lock held
if (listener != NULL) { if (consumerListener != NULL) {
listener->onBuffersReleased(); consumerListener->onBuffersReleased();
}
if (producerListener != NULL) {
for (int i : freedSlots) {
producerListener->onSlotFreed(i);
}
} }
return NO_ERROR; return NO_ERROR;
...@@ -165,7 +173,9 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { ...@@ -165,7 +173,9 @@ status_t BufferQueueProducer::setAsyncMode(bool async) {
ATRACE_CALL(); ATRACE_CALL();
BQ_LOGV("setAsyncMode: async = %d", async); BQ_LOGV("setAsyncMode: async = %d", async);
sp<IConsumerListener> listener; sp<IConsumerListener> consumerListener;
sp<IProducerListener> producerListener;
std::vector<int> freedSlots;
{ // Autolock scope { // Autolock scope
Mutex::Autolock lock(mCore->mMutex); Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked(); mCore->waitWhileAllocatingLocked();
...@@ -191,7 +201,7 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { ...@@ -191,7 +201,7 @@ status_t BufferQueueProducer::setAsyncMode(bool async) {
mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount) mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount)
- mCore->getMaxBufferCountLocked(); - mCore->getMaxBufferCountLocked();
if (!mCore->adjustAvailableSlotsLocked(delta)) { if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of " BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of "
"available slots. Delta = %d", delta); "available slots. Delta = %d", delta);
return BAD_VALUE; return BAD_VALUE;
...@@ -199,12 +209,20 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { ...@@ -199,12 +209,20 @@ status_t BufferQueueProducer::setAsyncMode(bool async) {
mCore->mAsyncMode = async; mCore->mAsyncMode = async;
VALIDATE_CONSISTENCY(); VALIDATE_CONSISTENCY();
mCore->mDequeueCondition.broadcast(); mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener; if (delta < 0) {
consumerListener = mCore->mConsumerListener;
producerListener = mCore->mConnectedProducerListener;
}
} // Autolock scope } // Autolock scope
// Call back without lock held // Call back without lock held
if (listener != NULL) { if (consumerListener != NULL) {
listener->onBuffersReleased(); consumerListener->onBuffersReleased();
}
if (producerListener != NULL) {
for (int i : freedSlots) {
producerListener->onSlotFreed(i);
}
} }
return NO_ERROR; return NO_ERROR;
} }
...@@ -361,6 +379,9 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, ...@@ -361,6 +379,9 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false; bool attachedByConsumer = false;
sp<IConsumerListener> consumerListener;
sp<IProducerListener> producerListener;
int found = BufferItem::INVALID_BUFFER_SLOT;
{ // Autolock scope { // Autolock scope
Mutex::Autolock lock(mCore->mMutex); Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked(); mCore->waitWhileAllocatingLocked();
...@@ -378,7 +399,6 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, ...@@ -378,7 +399,6 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
height = mCore->mDefaultHeight; height = mCore->mDefaultHeight;
} }
int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) { while (found == BufferItem::INVALID_BUFFER_SLOT) {
status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
&found); &found);
...@@ -408,6 +428,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, ...@@ -408,6 +428,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
mCore->mFreeSlots.insert(found); mCore->mFreeSlots.insert(found);
mCore->clearBufferSlotLocked(found); mCore->clearBufferSlotLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT; found = BufferItem::INVALID_BUFFER_SLOT;
consumerListener = mCore->mConsumerListener;
producerListener = mCore->mConnectedProducerListener;
continue; continue;
} }
} }
...@@ -444,6 +466,10 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, ...@@ -444,6 +466,10 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
if ((buffer == NULL) || if ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, usage)) buffer->needsReallocation(width, height, format, usage))
{ {
if (buffer != NULL) {
consumerListener = mCore->mConsumerListener;
producerListener = mCore->mConnectedProducerListener;
}
mSlots[found].mAcquireCalled = false; mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL; mSlots[found].mGraphicBuffer = NULL;
mSlots[found].mRequestBufferCalled = false; mSlots[found].mRequestBufferCalled = false;
...@@ -531,6 +557,14 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, ...@@ -531,6 +557,14 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
// Call back without lock held
if (consumerListener != NULL) {
consumerListener->onBuffersReleased();
}
if (producerListener != NULL) {
producerListener->onSlotFreed(found);
}
return returnFlags; return returnFlags;
} }
...@@ -538,43 +572,59 @@ status_t BufferQueueProducer::detachBuffer(int slot) { ...@@ -538,43 +572,59 @@ status_t BufferQueueProducer::detachBuffer(int slot) {
ATRACE_CALL(); ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot); ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot); BQ_LOGV("detachBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) { sp<IConsumerListener> consumerListener;
BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); sp<IProducerListener> producerListener;
return NO_INIT; {
} Mutex::Autolock lock(mCore->mMutex);
if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer: BufferQueue has no connected producer"); BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
return NO_INIT; return NO_INIT;
} }
if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) { if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer mode"); BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
return BAD_VALUE; return NO_INIT;
} }
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer "
slot, BufferQueueDefs::NUM_BUFFER_SLOTS); "mode");
return BAD_VALUE; return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) { }
BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
"(state = %s)", slot, mSlots[slot].mBufferState.string()); if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
return BAD_VALUE; BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
} else if (!mSlots[slot].mRequestBufferCalled) { slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
BQ_LOGE("detachBuffer: buffer in slot %d has not been requested", return BAD_VALUE;
slot); } else if (!mSlots[slot].mBufferState.isDequeued()) {
return BAD_VALUE; BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
"(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) {
BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
slot);
return BAD_VALUE;
}
mSlots[slot].mBufferState.detachProducer();
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
mCore->mDequeueCondition.broadcast();
VALIDATE_CONSISTENCY();
producerListener = mCore->mConnectedProducerListener;
consumerListener = mCore->mConsumerListener;
} }
mSlots[slot].mBufferState.detachProducer(); // Call back without lock held
mCore->mActiveBuffers.erase(slot); if (consumerListener != NULL) {
mCore->mFreeSlots.insert(slot); consumerListener->onBuffersReleased();
mCore->clearBufferSlotLocked(slot); }
mCore->mDequeueCondition.broadcast(); if (producerListener != NULL) {
VALIDATE_CONSISTENCY(); producerListener->onSlotFreed(slot);
}
return NO_ERROR; return NO_ERROR;
} }
...@@ -591,40 +641,54 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, ...@@ -591,40 +641,54 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
return BAD_VALUE; return BAD_VALUE;
} }
Mutex::Autolock lock(mCore->mMutex); sp<IConsumerListener> consumerListener;
sp<IProducerListener> producerListener;
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
{
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
if (mCore->mIsAbandoned) { if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned"); BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
return NO_INIT; return NO_INIT;
} }
if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { if (mCore->mSingleBufferMode) {
BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer"); BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
return NO_INIT; "mode");
} return BAD_VALUE;
}
if (mCore->mSingleBufferMode) { mCore->waitWhileAllocatingLocked();
BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
"mode");
return BAD_VALUE;
}
mCore->waitWhileAllocatingLocked(); if (mCore->mFreeBuffers.empty()) {
return NO_MEMORY;
}
if (mCore->mFreeBuffers.empty()) { found = mCore->mFreeBuffers.front();
return NO_MEMORY; mCore->mFreeBuffers.remove(found);
} mCore->mFreeSlots.insert(found);
int found = mCore->mFreeBuffers.front(); BQ_LOGV("detachNextBuffer detached slot %d", found);
mCore->mFreeBuffers.remove(found);
mCore->mFreeSlots.insert(found);
BQ_LOGV("detachNextBuffer detached slot %d", found); *outBuffer = mSlots[found].mGraphicBuffer;
*outFence = mSlots[found].mFence;
mCore->clearBufferSlotLocked(found);
VALIDATE_CONSISTENCY();
consumerListener = mCore->mConsumerListener;
producerListener = mCore->mConnectedProducerListener;
}
*outBuffer = mSlots[found].mGraphicBuffer; // Call back without lock held
*outFence = mSlots[found].mFence; if (consumerListener != NULL) {
mCore->clearBufferSlotLocked(found); consumerListener->onBuffersReleased();
VALIDATE_CONSISTENCY(); }
if (producerListener != NULL) {
producerListener->onSlotFreed(found);
}
return NO_ERROR; return NO_ERROR;
} }
...@@ -1020,82 +1084,102 @@ int BufferQueueProducer::query(int what, int *outValue) { ...@@ -1020,82 +1084,102 @@ int BufferQueueProducer::query(int what, int *outValue) {
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output) { int api, bool producerControlledByApp, QueueBufferOutput *output) {
ATRACE_CALL(); ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex); int status = NO_ERROR;
mConsumerName = mCore->mConsumerName; sp<IConsumerListener> consumerListener;
BQ_LOGV("connect: api=%d producerControlledByApp=%s", api, sp<IProducerListener> producerListener;
producerControlledByApp ? "true" : "false"); std::vector<int> freedSlots;
{
Mutex::Autolock lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
producerControlledByApp ? "true" : "false");
if (mCore->mIsAbandoned) { if (mCore->mIsAbandoned) {
BQ_LOGE("connect: BufferQueue has been abandoned"); BQ_LOGE("connect: BufferQueue has been abandoned");
return NO_INIT; return NO_INIT;
} }
if (mCore->mConsumerListener == NULL) { if (mCore->mConsumerListener == NULL) {
BQ_LOGE("connect: BufferQueue has no consumer"); BQ_LOGE("connect: BufferQueue has no consumer");
return NO_INIT; return NO_INIT;
} }
if (output == NULL) { if (output == NULL) {
BQ_LOGE("connect: output was NULL"); BQ_LOGE("connect: output was NULL");
return BAD_VALUE; return BAD_VALUE;
} }
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("connect: already connected (cur=%d req=%d)", BQ_LOGE("connect: already connected (cur=%d req=%d)",
mCore->mConnectedApi, api); mCore->mConnectedApi, api);
return BAD_VALUE; return BAD_VALUE;
} }
int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, bool dequeueBufferCannotBlock = mDequeueTimeout < 0 ?
mDequeueTimeout < 0 ? mCore->mConsumerControlledByApp && producerControlledByApp :
mCore->mConsumerControlledByApp && producerControlledByApp : false, false;
mCore->mMaxBufferCount) - int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
mCore->getMaxBufferCountLocked(); dequeueBufferCannotBlock, mCore->mMaxBufferCount) -
if (!mCore->adjustAvailableSlotsLocked(delta)) { mCore->getMaxBufferCountLocked();
BQ_LOGE("connect: BufferQueue failed to adjust the number of available "
"slots. Delta = %d", delta);
return BAD_VALUE;
}
int status = NO_ERROR; if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
switch (api) { BQ_LOGE("connect: BufferQueue failed to adjust the number of "
case NATIVE_WINDOW_API_EGL: "available slots. Delta = %d", delta);
case NATIVE_WINDOW_API_CPU: return BAD_VALUE;
case NATIVE_WINDOW_API_MEDIA: }
case NATIVE_WINDOW_API_CAMERA:
mCore->mConnectedApi = api; switch (api) {
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, case NATIVE_WINDOW_API_EGL:
mCore->mTransformHint, case NATIVE_WINDOW_API_CPU:
static_cast<uint32_t>(mCore->mQueue.size())); case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
// Set up a death notification so that we can disconnect mCore->mConnectedApi = api;
// automatically if the remote producer dies output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
if (listener != NULL && mCore->mTransformHint,
IInterface::asBinder(listener)->remoteBinder() != NULL) { static_cast<uint32_t>(mCore->mQueue.size()));
status = IInterface::asBinder(listener)->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this)); // Set up a death notification so that we can disconnect
if (status != NO_ERROR) { // automatically if the remote producer dies
BQ_LOGE("connect: linkToDeath failed: %s (%d)", if (listener != NULL &&
strerror(-status), status); IInterface::asBinder(listener)->remoteBinder() !=
NULL) {
status = IInterface::asBinder(listener)->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
if (status != NO_ERROR) {
BQ_LOGE("connect: linkToDeath failed: %s (%d)",
strerror(-status), status);
}
} }
} mCore->mConnectedProducerListener = listener;
mCore->mConnectedProducerListener = listener; break;
break; default:
default: BQ_LOGE("connect: unknown API %d", api);
BQ_LOGE("connect: unknown API %d", api); status = BAD_VALUE;
status = BAD_VALUE; break;
break; }
mCore->mBufferHasBeenQueued = false;
mCore->mDequeueBufferCannotBlock = dequeueBufferCannotBlock;
mCore->mAllowAllocation = true;
VALIDATE_CONSISTENCY();
if (delta < 0) {
consumerListener = mCore->mConsumerListener;
producerListener = listener;
}
} }
mCore->mBufferHasBeenQueued = false; // Call back without lock held
mCore->mDequeueBufferCannotBlock = false; if (consumerListener != NULL) {
if (mDequeueTimeout < 0) { consumerListener->onBuffersReleased();
mCore->mDequeueBufferCannotBlock = }
mCore->mConsumerControlledByApp && producerControlledByApp; if (producerListener != NULL) {
for (int i : freedSlots) {
producerListener->onSlotFreed(i);
}
} }
mCore->mAllowAllocation = true;
VALIDATE_CONSISTENCY();
return status; return status;
} }
...@@ -1313,19 +1397,40 @@ status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) { ...@@ -1313,19 +1397,40 @@ status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) {
ATRACE_CALL(); ATRACE_CALL();
BQ_LOGV("setDequeueTimeout: %" PRId64, timeout); BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
Mutex::Autolock lock(mCore->mMutex); sp<IConsumerListener> consumerListener;
int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false, sp<IProducerListener> producerListener;
mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); std::vector<int> freedSlots;
if (!mCore->adjustAvailableSlotsLocked(delta)) { {
BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of " Mutex::Autolock lock(mCore->mMutex);
"available slots. Delta = %d", delta); int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
return BAD_VALUE; mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number"
" of available slots. Delta = %d", delta);
return BAD_VALUE;
}
mDequeueTimeout = timeout;
mCore->mDequeueBufferCannotBlock = false;
VALIDATE_CONSISTENCY();
if (delta < 0) {
consumerListener = mCore->mConsumerListener;
producerListener = mCore->mConnectedProducerListener;
}
} }
mDequeueTimeout = timeout; // Call back without lock held
mCore->mDequeueBufferCannotBlock = false; if (consumerListener != NULL) {
consumerListener->onBuffersReleased();
}
if (producerListener != NULL) {
for (int i : freedSlots) {
producerListener->onSlotFreed(i);
}
}
VALIDATE_CONSISTENCY();
return NO_ERROR; return NO_ERROR;
} }
......
...@@ -22,6 +22,7 @@ namespace android { ...@@ -22,6 +22,7 @@ namespace android {
enum { enum {
ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION, ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
ON_SLOT_FREED,
}; };
class BpProducerListener : public BpInterface<IProducerListener> class BpProducerListener : public BpInterface<IProducerListener>
...@@ -37,6 +38,17 @@ public: ...@@ -37,6 +38,17 @@ public:
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY); remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
} }
virtual void onSlotFreed(int slot) {
Parcel data, reply;
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
data.writeInt32(slot);
status_t err = remote()->transact(ON_SLOT_FREED, data, &reply,
IBinder::FLAG_ONEWAY);
if (err != NO_ERROR) {
ALOGE("onSlotFreed failed to transact %d", err);
}
}
}; };
// Out-of-line virtual method definition to trigger vtable emission in this // Out-of-line virtual method definition to trigger vtable emission in this
...@@ -52,6 +64,12 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, ...@@ -52,6 +64,12 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data,
CHECK_INTERFACE(IProducerListener, data, reply); CHECK_INTERFACE(IProducerListener, data, reply);
onBufferReleased(); onBufferReleased();
return NO_ERROR; return NO_ERROR;
case ON_SLOT_FREED: {
CHECK_INTERFACE(IProducerListener, data, reply);
int slot = data.readInt32();
onSlotFreed(slot);
return NO_ERROR;
}
} }
return BBinder::onTransact(code, data, reply, flags); return BBinder::onTransact(code, data, reply, flags);
} }
......
...@@ -838,10 +838,6 @@ int Surface::setBufferCount(int bufferCount) ...@@ -838,10 +838,6 @@ int Surface::setBufferCount(int bufferCount)
} }
} }
if (err == NO_ERROR) {
freeAllBuffers();
}
ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s", ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s",
bufferCount, strerror(-err)); bufferCount, strerror(-err));
...@@ -858,10 +854,6 @@ int Surface::setMaxDequeuedBufferCount(int maxDequeuedBuffers) { ...@@ -858,10 +854,6 @@ int Surface::setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) " ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) "
"returned %s", maxDequeuedBuffers, strerror(-err)); "returned %s", maxDequeuedBuffers, strerror(-err));
if (err == NO_ERROR) {
freeAllBuffers();
}
return err; return err;
} }
...@@ -874,10 +866,6 @@ int Surface::setAsyncMode(bool async) { ...@@ -874,10 +866,6 @@ int Surface::setAsyncMode(bool async) {
ALOGE_IF(err, "IGraphicBufferProducer::setAsyncMode(%d) returned %s", ALOGE_IF(err, "IGraphicBufferProducer::setAsyncMode(%d) returned %s",
async, strerror(-err)); async, strerror(-err));
if (err == NO_ERROR) {
freeAllBuffers();
}
return err; return err;
} }
......
...@@ -727,4 +727,55 @@ TEST_F(IGraphicBufferProducerTest, ...@@ -727,4 +727,55 @@ TEST_F(IGraphicBufferProducerTest,
ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
} }
struct TestListener : public BnProducerListener {
virtual void onBufferReleased() {}
virtual void onSlotFreed(int slot) {
ASSERT_EQ(1, slot);
}
};
TEST_F(IGraphicBufferProducerTest, SlotFreedListenerReturnsCorrectSlot) {
const ::testing::TestInfo* const testInfo =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
sp<DummyConsumer> consumerListener = new DummyConsumer;
ASSERT_OK(mConsumer->consumerConnect(consumerListener, false));
sp<TestListener> producerListener = new TestListener;
IGraphicBufferProducer::QueueBufferOutput output;
ASSERT_OK(mProducer->connect(producerListener, TEST_API,
TEST_CONTROLLED_BY_APP, &output));
ASSERT_OK(mProducer->setMaxDequeuedBufferCount(2));
DequeueBufferResult buffer0;
sp<GraphicBuffer> buf;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
TEST_PRODUCER_USAGE_BITS, &buffer0));
ASSERT_OK(mProducer->requestBuffer(buffer0.slot, &buf));
DequeueBufferResult buffer1;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
TEST_PRODUCER_USAGE_BITS, &buffer1));
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
ASSERT_OK(mProducer->queueBuffer(buffer0.slot, input, &output));
DequeueBufferResult buffer2;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
TEST_PRODUCER_USAGE_BITS, &buffer2));
ASSERT_OK(mProducer->cancelBuffer(buffer1.slot, Fence::NO_FENCE));
ASSERT_OK(mProducer->setMaxDequeuedBufferCount(1));
}
} // namespace android } // namespace android
...@@ -232,4 +232,30 @@ TEST_F(SurfaceTest, GetConsumerName) { ...@@ -232,4 +232,30 @@ TEST_F(SurfaceTest, GetConsumerName) {
EXPECT_STREQ("TestConsumer", surface->getConsumerName().string()); EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
} }
TEST_F(SurfaceTest, DynamicSetBufferCount) {
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
sp<DummyConsumer> dummyConsumer(new DummyConsumer);
consumer->consumerConnect(dummyConsumer, false);
consumer->setConsumerName(String8("TestConsumer"));
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(),
NATIVE_WINDOW_API_CPU));
native_window_set_buffer_count(window.get(), 4);
int fence;
ANativeWindowBuffer* buffer;
ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
native_window_set_buffer_count(window.get(), 3);
ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
native_window_set_buffer_count(window.get(), 2);
ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment