BufferQueue: release mutex while allocating.
BufferQueueProducer::allocateBuffers used to keep the BufferQueueCore mutex while doing the buffer allocation, which would cause the consumer (which also needs the mutex) to block if the allocation takes a long time. Instead, release the mutex while doing the allocation, and grab it again before filling the slots. Keep a bool state and a condvar to prevent other producers from trying to allocate the slots while the mutex is released. Bug: 11792166 Change-Id: I4ab1319995ef892be2beba892f1fdbf50ce0416d
This commit is contained in:
parent
134a6a2594
commit
ea96044470
|
@ -120,6 +120,9 @@ private:
|
||||||
// in one of the slots.
|
// in one of the slots.
|
||||||
bool stillTracking(const BufferItem* item) const;
|
bool stillTracking(const BufferItem* item) const;
|
||||||
|
|
||||||
|
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
|
||||||
|
void waitWhileAllocatingLocked() const;
|
||||||
|
|
||||||
// mAllocator is the connection to SurfaceFlinger that is used to allocate
|
// mAllocator is the connection to SurfaceFlinger that is used to allocate
|
||||||
// new GraphicBuffer objects.
|
// new GraphicBuffer objects.
|
||||||
sp<IGraphicBufferAlloc> mAllocator;
|
sp<IGraphicBufferAlloc> mAllocator;
|
||||||
|
@ -234,6 +237,15 @@ private:
|
||||||
// mSidebandStream is a handle to the sideband buffer stream, if any
|
// mSidebandStream is a handle to the sideband buffer stream, if any
|
||||||
sp<NativeHandle> mSidebandStream;
|
sp<NativeHandle> mSidebandStream;
|
||||||
|
|
||||||
|
// mIsAllocating indicates whether a producer is currently trying to allocate buffers (which
|
||||||
|
// releases mMutex while doing the allocation proper). Producers should not modify any of the
|
||||||
|
// FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to
|
||||||
|
// false.
|
||||||
|
bool mIsAllocating;
|
||||||
|
|
||||||
|
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
|
||||||
|
// becomes false.
|
||||||
|
mutable Condition mIsAllocatingCondition;
|
||||||
}; // class BufferQueueCore
|
}; // class BufferQueueCore
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
|
@ -64,7 +64,9 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
|
||||||
mMaxAcquiredBufferCount(1),
|
mMaxAcquiredBufferCount(1),
|
||||||
mBufferHasBeenQueued(false),
|
mBufferHasBeenQueued(false),
|
||||||
mFrameCounter(0),
|
mFrameCounter(0),
|
||||||
mTransformHint(0)
|
mTransformHint(0),
|
||||||
|
mIsAllocating(false),
|
||||||
|
mIsAllocatingCondition()
|
||||||
{
|
{
|
||||||
if (allocator == NULL) {
|
if (allocator == NULL) {
|
||||||
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
|
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
|
||||||
|
@ -226,4 +228,11 @@ bool BufferQueueCore::stillTracking(const BufferItem* item) const {
|
||||||
(item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
|
(item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferQueueCore::waitWhileAllocatingLocked() const {
|
||||||
|
ATRACE_CALL();
|
||||||
|
while (mIsAllocating) {
|
||||||
|
mIsAllocatingCondition.wait(mMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
|
@ -74,6 +74,7 @@ status_t BufferQueueProducer::setBufferCount(int bufferCount) {
|
||||||
sp<IConsumerListener> listener;
|
sp<IConsumerListener> listener;
|
||||||
{ // Autolock scope
|
{ // Autolock scope
|
||||||
Mutex::Autolock lock(mCore->mMutex);
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
mCore->waitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (mCore->mIsAbandoned) {
|
if (mCore->mIsAbandoned) {
|
||||||
BQ_LOGE("setBufferCount: BufferQueue has been abandoned");
|
BQ_LOGE("setBufferCount: BufferQueue has been abandoned");
|
||||||
|
@ -266,6 +267,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
|
||||||
|
|
||||||
{ // Autolock scope
|
{ // Autolock scope
|
||||||
Mutex::Autolock lock(mCore->mMutex);
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
mCore->waitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (format == 0) {
|
if (format == 0) {
|
||||||
format = mCore->mDefaultBufferFormat;
|
format = mCore->mDefaultBufferFormat;
|
||||||
|
@ -424,6 +426,7 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::Autolock lock(mCore->mMutex);
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
mCore->waitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (mCore->mIsAbandoned) {
|
if (mCore->mIsAbandoned) {
|
||||||
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
|
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
|
||||||
|
@ -468,6 +471,7 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot,
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::Autolock lock(mCore->mMutex);
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
mCore->waitWhileAllocatingLocked();
|
||||||
|
|
||||||
status_t returnFlags = NO_ERROR;
|
status_t returnFlags = NO_ERROR;
|
||||||
int found;
|
int found;
|
||||||
|
@ -796,6 +800,7 @@ status_t BufferQueueProducer::disconnect(int api) {
|
||||||
sp<IConsumerListener> listener;
|
sp<IConsumerListener> listener;
|
||||||
{ // Autolock scope
|
{ // Autolock scope
|
||||||
Mutex::Autolock lock(mCore->mMutex);
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
mCore->waitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (mCore->mIsAbandoned) {
|
if (mCore->mIsAbandoned) {
|
||||||
// It's not really an error to disconnect after the surface has
|
// It's not really an error to disconnect after the surface has
|
||||||
|
@ -862,55 +867,101 @@ status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream)
|
||||||
|
|
||||||
void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
|
void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
|
||||||
uint32_t height, uint32_t format, uint32_t usage) {
|
uint32_t height, uint32_t format, uint32_t usage) {
|
||||||
Vector<int> freeSlots;
|
ATRACE_CALL();
|
||||||
|
while (true) {
|
||||||
|
Vector<int> freeSlots;
|
||||||
|
size_t newBufferCount = 0;
|
||||||
|
uint32_t allocWidth = 0;
|
||||||
|
uint32_t allocHeight = 0;
|
||||||
|
uint32_t allocFormat = 0;
|
||||||
|
uint32_t allocUsage = 0;
|
||||||
|
{ // Autolock scope
|
||||||
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
mCore->waitWhileAllocatingLocked();
|
||||||
|
|
||||||
Mutex::Autolock lock(mCore->mMutex);
|
int currentBufferCount = 0;
|
||||||
|
for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
||||||
|
if (mSlots[slot].mGraphicBuffer != NULL) {
|
||||||
|
++currentBufferCount;
|
||||||
|
} else {
|
||||||
|
if (mSlots[slot].mBufferState != BufferSlot::FREE) {
|
||||||
|
BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
|
||||||
|
slot);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int currentBufferCount = 0;
|
freeSlots.push_front(slot);
|
||||||
for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
}
|
||||||
if (mSlots[slot].mGraphicBuffer != NULL) {
|
}
|
||||||
++currentBufferCount;
|
|
||||||
} else {
|
int maxBufferCount = mCore->getMaxBufferCountLocked(async);
|
||||||
if (mSlots[slot].mBufferState != BufferSlot::FREE) {
|
BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
|
||||||
BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
|
currentBufferCount, maxBufferCount);
|
||||||
slot);
|
if (maxBufferCount <= currentBufferCount)
|
||||||
|
return;
|
||||||
|
newBufferCount = maxBufferCount - currentBufferCount;
|
||||||
|
if (freeSlots.size() < newBufferCount) {
|
||||||
|
BQ_LOGE("allocateBuffers: ran out of free slots");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
allocWidth = width > 0 ? width : mCore->mDefaultWidth;
|
||||||
|
allocHeight = height > 0 ? height : mCore->mDefaultHeight;
|
||||||
|
allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
|
||||||
|
allocUsage = usage | mCore->mConsumerUsageBits;
|
||||||
|
|
||||||
|
mCore->mIsAllocating = true;
|
||||||
|
} // Autolock scope
|
||||||
|
|
||||||
|
Vector<sp<GraphicBuffer> > buffers;
|
||||||
|
for (size_t i = 0; i < newBufferCount; ++i) {
|
||||||
|
status_t result = NO_ERROR;
|
||||||
|
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
|
||||||
|
allocWidth, allocHeight, allocFormat, allocUsage, &result));
|
||||||
|
if (result != NO_ERROR) {
|
||||||
|
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
|
||||||
|
" %u, usage %u)", width, height, format, usage);
|
||||||
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
mCore->mIsAllocating = false;
|
||||||
|
mCore->mIsAllocatingCondition.broadcast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffers.push_back(graphicBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Autolock scope
|
||||||
|
Mutex::Autolock lock(mCore->mMutex);
|
||||||
|
uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
|
||||||
|
uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
|
||||||
|
uint32_t checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
|
||||||
|
uint32_t checkUsage = usage | mCore->mConsumerUsageBits;
|
||||||
|
if (checkWidth != allocWidth || checkHeight != allocHeight ||
|
||||||
|
checkFormat != allocFormat || checkUsage != allocUsage) {
|
||||||
|
// Something changed while we released the lock. Retry.
|
||||||
|
BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying.");
|
||||||
|
mCore->mIsAllocating = false;
|
||||||
|
mCore->mIsAllocatingCondition.broadcast();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
freeSlots.push_front(slot);
|
for (size_t i = 0; i < newBufferCount; ++i) {
|
||||||
}
|
int slot = freeSlots[i];
|
||||||
}
|
if (mSlots[slot].mBufferState != BufferSlot::FREE) {
|
||||||
|
// A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
|
||||||
|
// allocated.
|
||||||
|
BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
|
||||||
|
"Dropping allocated buffer.", slot);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mCore->freeBufferLocked(slot); // Clean up the slot first
|
||||||
|
mSlots[slot].mGraphicBuffer = buffers[i];
|
||||||
|
mSlots[slot].mFrameNumber = 0;
|
||||||
|
mSlots[slot].mFence = Fence::NO_FENCE;
|
||||||
|
BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
|
||||||
|
}
|
||||||
|
|
||||||
int maxBufferCount = mCore->getMaxBufferCountLocked(async);
|
mCore->mIsAllocating = false;
|
||||||
BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
|
mCore->mIsAllocatingCondition.broadcast();
|
||||||
currentBufferCount, maxBufferCount);
|
} // Autolock scope
|
||||||
for (; currentBufferCount < maxBufferCount; ++currentBufferCount) {
|
|
||||||
if (freeSlots.empty()) {
|
|
||||||
BQ_LOGE("allocateBuffers: ran out of free slots");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
width = width > 0 ? width : mCore->mDefaultWidth;
|
|
||||||
height = height > 0 ? height : mCore->mDefaultHeight;
|
|
||||||
format = format != 0 ? format : mCore->mDefaultBufferFormat;
|
|
||||||
usage |= mCore->mConsumerUsageBits;
|
|
||||||
|
|
||||||
status_t result = NO_ERROR;
|
|
||||||
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
|
|
||||||
width, height, format, usage, &result));
|
|
||||||
if (result != NO_ERROR) {
|
|
||||||
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
|
|
||||||
" %u, usage %u)", width, height, format, usage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int slot = freeSlots[freeSlots.size() - 1];
|
|
||||||
mCore->freeBufferLocked(slot); // Clean up the slot first
|
|
||||||
mSlots[slot].mGraphicBuffer = graphicBuffer;
|
|
||||||
mSlots[slot].mFrameNumber = 0;
|
|
||||||
mSlots[slot].mFence = Fence::NO_FENCE;
|
|
||||||
BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
|
|
||||||
freeSlots.pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue