libgui: Allow an IGBProducer to disable allocation
Adds a new method IGBP::allowAllocation, which controls whether dequeueBuffer is permitted to allocate a new buffer. If allocation is disallowed, dequeueBuffer will block or return an error as it normally would (as controlled by *ControlledByApp). If there are free buffers, but they are not of the correct dimensions, format, or usage, they may be freed if a more suitable buffer is not found first. Bug: 19801715 Change-Id: I0d604958b78b2fd775c2547690301423f9a52165
This commit is contained in:
parent
0de7ea7529
commit
9de7293b0a
@ -266,6 +266,10 @@ private:
|
||||
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
|
||||
// becomes false.
|
||||
mutable Condition mIsAllocatingCondition;
|
||||
|
||||
// mAllowAllocation determines whether dequeueBuffer is allowed to allocate
|
||||
// new buffers
|
||||
bool mAllowAllocation;
|
||||
}; // class BufferQueueCore
|
||||
|
||||
} // namespace android
|
||||
|
@ -172,6 +172,9 @@ public:
|
||||
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
|
||||
PixelFormat format, uint32_t usage);
|
||||
|
||||
// See IGraphicBufferProducer::allowAllocation
|
||||
virtual status_t allowAllocation(bool allow);
|
||||
|
||||
private:
|
||||
// This is required by the IBinder::DeathRecipient interface
|
||||
virtual void binderDied(const wp<IBinder>& who);
|
||||
|
@ -458,6 +458,18 @@ public:
|
||||
// allocated, this function has no effect.
|
||||
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
|
||||
PixelFormat format, uint32_t usage) = 0;
|
||||
|
||||
// Sets whether dequeueBuffer is allowed to allocate new buffers.
|
||||
//
|
||||
// Normally dequeueBuffer does not discriminate between free slots which
|
||||
// already have an allocated buffer and those which do not, and will
|
||||
// allocate a new buffer if the slot doesn't have a buffer or if the slot's
|
||||
// buffer doesn't match the requested size, format, or usage. This method
|
||||
// allows the producer to restrict the eligible slots to those which already
|
||||
// have an allocated buffer of the correct size, format, and usage. If no
|
||||
// eligible slot is available, dequeueBuffer will block or return an error
|
||||
// as usual.
|
||||
virtual status_t allowAllocation(bool allow) = 0;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -97,6 +97,9 @@ public:
|
||||
status_t reallocate(uint32_t inWidth, uint32_t inHeight,
|
||||
PixelFormat inFormat, uint32_t inUsage);
|
||||
|
||||
bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
|
||||
PixelFormat inFormat, uint32_t inUsage);
|
||||
|
||||
status_t lock(uint32_t inUsage, void** vaddr);
|
||||
status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
|
||||
// For HAL_PIXEL_FORMAT_YCbCr_420_888
|
||||
|
@ -69,7 +69,8 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
|
||||
mFrameCounter(0),
|
||||
mTransformHint(0),
|
||||
mIsAllocating(false),
|
||||
mIsAllocatingCondition()
|
||||
mIsAllocatingCondition(),
|
||||
mAllowAllocation(true)
|
||||
{
|
||||
if (allocator == NULL) {
|
||||
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
|
||||
|
@ -219,7 +219,7 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
|
||||
auto slot = mCore->mFreeBuffers.begin();
|
||||
*found = *slot;
|
||||
mCore->mFreeBuffers.erase(slot);
|
||||
} else if (!mCore->mFreeSlots.empty()) {
|
||||
} else if (mCore->mAllowAllocation && !mCore->mFreeSlots.empty()) {
|
||||
auto slot = mCore->mFreeSlots.begin();
|
||||
// Only return free slots up to the max buffer count
|
||||
if (*slot < maxBufferCount) {
|
||||
@ -285,17 +285,39 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
|
||||
// Enable the usage bits the consumer requested
|
||||
usage |= mCore->mConsumerUsageBits;
|
||||
|
||||
int found;
|
||||
status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
|
||||
&found, &returnFlags);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
const bool useDefaultSize = !width && !height;
|
||||
if (useDefaultSize) {
|
||||
width = mCore->mDefaultWidth;
|
||||
height = mCore->mDefaultHeight;
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
BQ_LOGE("dequeueBuffer: no available buffer slots");
|
||||
return -EBUSY;
|
||||
int found = BufferItem::INVALID_BUFFER_SLOT;
|
||||
while (found == BufferItem::INVALID_BUFFER_SLOT) {
|
||||
status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
|
||||
&found, &returnFlags);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
BQ_LOGE("dequeueBuffer: no available buffer slots");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
|
||||
|
||||
// If we are not allowed to allocate new buffers,
|
||||
// waitForFreeSlotThenRelock must have returned a slot containing a
|
||||
// buffer. If this buffer would require reallocation to meet the
|
||||
// requested attributes, we free it and attempt to get another one.
|
||||
if (!mCore->mAllowAllocation) {
|
||||
if (buffer->needsReallocation(width, height, format, usage)) {
|
||||
mCore->freeBufferLocked(found);
|
||||
found = BufferItem::INVALID_BUFFER_SLOT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*outSlot = found;
|
||||
@ -303,20 +325,11 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
|
||||
|
||||
attachedByConsumer = mSlots[found].mAttachedByConsumer;
|
||||
|
||||
const bool useDefaultSize = !width && !height;
|
||||
if (useDefaultSize) {
|
||||
width = mCore->mDefaultWidth;
|
||||
height = mCore->mDefaultHeight;
|
||||
}
|
||||
|
||||
mSlots[found].mBufferState = BufferSlot::DEQUEUED;
|
||||
|
||||
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
|
||||
if ((buffer == NULL) ||
|
||||
(static_cast<uint32_t>(buffer->width) != width) ||
|
||||
(static_cast<uint32_t>(buffer->height) != height) ||
|
||||
(buffer->format != format) ||
|
||||
((static_cast<uint32_t>(buffer->usage) & usage) != usage))
|
||||
buffer->needsReallocation(width, height, format, usage))
|
||||
{
|
||||
mSlots[found].mAcquireCalled = false;
|
||||
mSlots[found].mGraphicBuffer = NULL;
|
||||
@ -933,6 +946,12 @@ void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (!mCore->mAllowAllocation) {
|
||||
BQ_LOGE("allocateBuffers: allocation is not allowed for this "
|
||||
"BufferQueue");
|
||||
return;
|
||||
}
|
||||
|
||||
int currentBufferCount = 0;
|
||||
for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
||||
if (mSlots[slot].mGraphicBuffer != NULL) {
|
||||
@ -1027,6 +1046,15 @@ void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
|
||||
}
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::allowAllocation(bool allow) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("allowAllocation: %s", allow ? "true" : "false");
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mAllowAllocation = allow;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
|
||||
// If we're here, it means that a producer we were connected to died.
|
||||
// We're guaranteed that we are still connected to it because we remove
|
||||
|
@ -46,6 +46,7 @@ enum {
|
||||
DISCONNECT,
|
||||
SET_SIDEBAND_STREAM,
|
||||
ALLOCATE_BUFFERS,
|
||||
ALLOW_ALLOCATION,
|
||||
};
|
||||
|
||||
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
|
||||
@ -271,6 +272,18 @@ public:
|
||||
ALOGE("allocateBuffers failed to transact: %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
virtual status_t allowAllocation(bool allow) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
|
||||
data.writeInt32(static_cast<int32_t>(allow));
|
||||
status_t result = remote()->transact(ALLOW_ALLOCATION, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
result = reply.readInt32();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Out-of-line virtual method definition to trigger vtable emission in this
|
||||
@ -418,7 +431,7 @@ status_t BnGraphicBufferProducer::onTransact(
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
}
|
||||
case ALLOCATE_BUFFERS:
|
||||
case ALLOCATE_BUFFERS: {
|
||||
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
|
||||
bool async = static_cast<bool>(data.readInt32());
|
||||
uint32_t width = data.readUint32();
|
||||
@ -427,6 +440,14 @@ status_t BnGraphicBufferProducer::onTransact(
|
||||
uint32_t usage = data.readUint32();
|
||||
allocateBuffers(async, width, height, format, usage);
|
||||
return NO_ERROR;
|
||||
}
|
||||
case ALLOW_ALLOCATION: {
|
||||
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
|
||||
bool allow = static_cast<bool>(data.readInt32());
|
||||
status_t result = allowAllocation(allow);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
@ -364,4 +364,40 @@ TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
|
||||
ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
|
||||
}
|
||||
|
||||
TEST_F(BufferQueueTest, TestDisallowingAllocation) {
|
||||
createBufferQueue();
|
||||
sp<DummyConsumer> dc(new DummyConsumer);
|
||||
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
|
||||
IGraphicBufferProducer::QueueBufferOutput output;
|
||||
ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
|
||||
NATIVE_WINDOW_API_CPU, true, &output));
|
||||
|
||||
static const uint32_t WIDTH = 320;
|
||||
static const uint32_t HEIGHT = 240;
|
||||
|
||||
ASSERT_EQ(OK, mConsumer->setDefaultBufferSize(WIDTH, HEIGHT));
|
||||
|
||||
int slot;
|
||||
sp<Fence> fence;
|
||||
sp<GraphicBuffer> buffer;
|
||||
// This should return an error since it would require an allocation
|
||||
ASSERT_EQ(OK, mProducer->allowAllocation(false));
|
||||
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, false, 0, 0,
|
||||
0, GRALLOC_USAGE_SW_WRITE_OFTEN));
|
||||
|
||||
// This should succeed, now that we've lifted the prohibition
|
||||
ASSERT_EQ(OK, mProducer->allowAllocation(true));
|
||||
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
|
||||
mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN));
|
||||
|
||||
// Release the previous buffer back to the BufferQueue
|
||||
mProducer->cancelBuffer(slot, fence);
|
||||
|
||||
// This should fail since we're requesting a different size
|
||||
ASSERT_EQ(OK, mProducer->allowAllocation(false));
|
||||
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, false,
|
||||
WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
@ -152,6 +152,16 @@ status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
|
||||
return initSize(inWidth, inHeight, inFormat, inUsage);
|
||||
}
|
||||
|
||||
bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight,
|
||||
PixelFormat inFormat, uint32_t inUsage)
|
||||
{
|
||||
if (static_cast<int>(inWidth) != width) return true;
|
||||
if (static_cast<int>(inHeight) != height) return true;
|
||||
if (inFormat != format) return true;
|
||||
if ((static_cast<uint32_t>(usage) & inUsage) != inUsage) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,
|
||||
PixelFormat inFormat, uint32_t inUsage)
|
||||
{
|
||||
|
@ -526,6 +526,10 @@ void VirtualDisplaySurface::allocateBuffers(bool /* async */,
|
||||
// TODO: Should we actually allocate buffers for a virtual display?
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::allowAllocation(bool /* allow */) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
void VirtualDisplaySurface::updateQueueBufferOutput(
|
||||
const QueueBufferOutput& qbo) {
|
||||
uint32_t w, h, transformHint, numPendingBuffers;
|
||||
|
@ -115,6 +115,7 @@ private:
|
||||
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
|
||||
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
|
||||
PixelFormat format, uint32_t usage);
|
||||
virtual status_t allowAllocation(bool allow);
|
||||
|
||||
//
|
||||
// Utility methods
|
||||
|
@ -110,6 +110,10 @@ void MonitoredProducer::allocateBuffers(bool async, uint32_t width,
|
||||
mProducer->allocateBuffers(async, width, height, format, usage);
|
||||
}
|
||||
|
||||
status_t MonitoredProducer::allowAllocation(bool allow) {
|
||||
return mProducer->allowAllocation(allow);
|
||||
}
|
||||
|
||||
IBinder* MonitoredProducer::onAsBinder() {
|
||||
return IInterface::asBinder(mProducer).get();
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ public:
|
||||
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
|
||||
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
|
||||
PixelFormat format, uint32_t usage);
|
||||
virtual status_t allowAllocation(bool allow);
|
||||
virtual IBinder* onAsBinder();
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user