libgui: Add dequeue/attach timeout
Adds the ability to specify the timeout when dequeueBuffer or
attachBuffer block due to the lack of a free buffer/slot. By default,
these will block indefinitely (which is signified by a timeout of -1).
When a timeout (other than -1) is specified, non-blocking mode is
disabled and the given timeout will be used instead.
Bug: 25196773
Change-Id: I17fdbeebccb7c8d878703d758ac1209608258e61
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 5fe5ce0..835593f 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -174,7 +174,10 @@
virtual uint64_t getNextFrameNumber() const override;
// See IGraphicBufferProducer::setSingleBufferMode
- virtual status_t setSingleBufferMode(bool singleBufferMode);
+ virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+
+ // See IGraphicBufferProducer::setDequeueTimeout
+ virtual status_t setDequeueTimeout(nsecs_t timeout) override;
private:
// This is required by the IBinder::DeathRecipient interface
@@ -216,6 +219,10 @@
int mCurrentCallbackTicket; // Protected by mCallbackMutex
Condition mCallbackCondition;
+ // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
+ // slot is not yet available.
+ nsecs_t mDequeueTimeout;
+
}; // class BufferQueueProducer
} // namespace android
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index d6daca7..8646981 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -177,6 +177,8 @@
// * WOULD_BLOCK - no buffer is currently available, and blocking is disabled
// since both the producer/consumer are controlled by app
// * NO_MEMORY - out of memory, cannot allocate the graphics buffer.
+ // * TIMED_OUT - the timeout set by setDequeueTimeout was exceeded while
+ // waiting for a buffer to become available.
//
// All other negative values are an unknown error returned downstream
// from the graphics allocator (typically errno).
@@ -248,6 +250,8 @@
// * WOULD_BLOCK - no buffer slot is currently available, and blocking is
// disabled since both the producer/consumer are
// controlled by the app.
+ // * TIMED_OUT - the timeout set by setDequeueTimeout was exceeded while
+ // waiting for a slot to become available.
virtual status_t attachBuffer(int* outSlot,
const sp<GraphicBuffer>& buffer) = 0;
@@ -518,6 +522,19 @@
// and returned to all calls to dequeueBuffer and acquireBuffer. This allows
// the producer and consumer to simultaneously access the same buffer.
virtual status_t setSingleBufferMode(bool singleBufferMode) = 0;
+
+ // Sets how long dequeueBuffer will wait for a buffer to become available
+ // before returning an error (TIMED_OUT).
+ //
+ // This timeout also affects the attachBuffer call, which will block if
+ // there is not a free slot available into which the attached buffer can be
+ // placed.
+ //
+ // By default, the BufferQueue will wait forever, which is indicated by a
+ // timeout of -1. If set (to a value other than -1), this will disable
+ // non-blocking mode and its corresponding spare buffer (which is used to
+ // ensure a buffer is always available).
+ virtual status_t setDequeueTimeout(nsecs_t timeout) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index bcc3fff..f79210b 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -117,6 +117,9 @@
* in <system/window.h>. */
int setScalingMode(int mode);
+ // See IGraphicBufferProducer::setDequeueTimeout
+ status_t setDequeueTimeout(nsecs_t timeout);
+
protected:
virtual ~Surface();
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9271ed8..c48f237 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -43,7 +43,8 @@
mCallbackMutex(),
mNextCallbackTicket(0),
mCurrentCallbackTicket(0),
- mCallbackCondition() {}
+ mCallbackCondition(),
+ mDequeueTimeout(-1) {}
BufferQueueProducer::~BufferQueueProducer() {}
@@ -271,7 +272,15 @@
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
return WOULD_BLOCK;
}
- mCore->mDequeueCondition.wait(mCore->mMutex);
+ if (mDequeueTimeout >= 0) {
+ status_t result = mCore->mDequeueCondition.waitRelative(
+ mCore->mMutex, mDequeueTimeout);
+ if (result == TIMED_OUT) {
+ return result;
+ }
+ } else {
+ mCore->mDequeueCondition.wait(mCore->mMutex);
+ }
}
} // while (tryAgain)
@@ -1012,8 +1021,11 @@
}
mCore->mBufferHasBeenQueued = false;
- mCore->mDequeueBufferCannotBlock = mCore->mConsumerControlledByApp &&
- producerControlledByApp;
+ mCore->mDequeueBufferCannotBlock = false;
+ if (mDequeueTimeout < 0) {
+ mCore->mDequeueBufferCannotBlock =
+ mCore->mConsumerControlledByApp && producerControlledByApp;
+ }
mCore->mAllowAllocation = true;
return status;
@@ -1247,7 +1259,16 @@
mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
}
mCore->mSingleBufferMode = singleBufferMode;
+ return NO_ERROR;
+}
+status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) {
+ ATRACE_CALL();
+ BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
+
+ Mutex::Autolock lock(mCore->mMutex);
+ mDequeueTimeout = timeout;
+ mCore->mDequeueBufferCannotBlock = false;
return NO_ERROR;
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index d5310bd..0cca58d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -51,7 +51,8 @@
SET_MAX_DEQUEUED_BUFFER_COUNT,
SET_ASYNC_MODE,
GET_NEXT_FRAME_NUMBER,
- SET_SINGLE_BUFFER_MODE
+ SET_SINGLE_BUFFER_MODE,
+ SET_DEQUEUE_TIMEOUT,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -353,6 +354,18 @@
}
return result;
}
+
+ virtual status_t setDequeueTimeout(nsecs_t timeout) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt64(timeout);
+ status_t result = remote()->transact(SET_DEQUEUE_TIMEOUT, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setDequeueTimeout failed to transact: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -548,6 +561,13 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case SET_DEQUEUE_TIMEOUT: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ nsecs_t timeout = data.readInt64();
+ status_t result = setDequeueTimeout(timeout);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index b816ff3..9e90ad0 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -119,6 +119,10 @@
return mGraphicBufferProducer->getConsumerName();
}
+status_t Surface::setDequeueTimeout(nsecs_t timeout) {
+ return mGraphicBufferProducer->setDequeueTimeout(timeout);
+}
+
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
Surface* c = getSelf(window);
return c->setSwapInterval(interval);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index a32a90c..a45a5be 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -535,4 +535,66 @@
}
}
+TEST_F(BufferQueueTest, TestTimeouts) {
+ 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));
+
+ // Fill up the queue. Since the controlledByApp flags are set to true, this
+ // queue should be in non-blocking mode, and we should be recycling the same
+ // two buffers
+ for (int i = 0; i < 5; ++i) {
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0);
+ if (i < 2) {
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ result);
+ } else {
+ ASSERT_EQ(OK, result);
+ }
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ IGraphicBufferProducer::QueueBufferOutput output{};
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ }
+
+ const auto TIMEOUT = ms2ns(250);
+ mProducer->setDequeueTimeout(TIMEOUT);
+
+ // Setting a timeout will change the BufferQueue into blocking mode (with
+ // one droppable buffer in the queue and one free from the previous
+ // dequeue/queues), so dequeue and queue two more buffers: one to replace
+ // the current droppable buffer, and a second to max out the buffer count
+ sp<GraphicBuffer> buffer; // Save a buffer to attach later
+ for (int i = 0; i < 2; ++i) {
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ }
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ auto startTime = systemTime();
+ ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_GE(systemTime() - startTime, TIMEOUT);
+
+ // We're technically attaching the same buffer multiple times (since we
+ // queued it previously), but that doesn't matter for this test
+ startTime = systemTime();
+ ASSERT_EQ(TIMED_OUT, mProducer->attachBuffer(&slot, buffer));
+ ASSERT_GE(systemTime() - startTime, TIMEOUT);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index f8fe8e1..64c1dd9 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -551,6 +551,11 @@
return mSource[SOURCE_SINK]->setSingleBufferMode(singleBufferMode);
}
+status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) {
+ ALOGE("setDequeueTimeout not supported on VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
void VirtualDisplaySurface::updateQueueBufferOutput(
const QueueBufferOutput& qbo) {
uint32_t w, h, transformHint, numPendingBuffers;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index bde7fcf..7f451a9 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -120,7 +120,8 @@
virtual status_t setGenerationNumber(uint32_t generationNumber);
virtual String8 getConsumerName() const override;
virtual uint64_t getNextFrameNumber() const override;
- virtual status_t setSingleBufferMode(bool singleBufferMode);
+ virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+ virtual status_t setDequeueTimeout(nsecs_t timeout) override;
//
// Utility methods
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 16879ca..efc44ab 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -135,6 +135,10 @@
return mProducer->setSingleBufferMode(singleBufferMode);
}
+status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
+ return mProducer->setDequeueTimeout(timeout);
+}
+
IBinder* MonitoredProducer::onAsBinder() {
return IInterface::asBinder(mProducer).get();
}
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index c2b2e24..aea2e39 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -58,6 +58,7 @@
virtual status_t setGenerationNumber(uint32_t generationNumber);
virtual String8 getConsumerName() const override;
virtual uint64_t getNextFrameNumber() const override;
+ virtual status_t setDequeueTimeout(nsecs_t timeout) override;
virtual IBinder* onAsBinder();
virtual status_t setSingleBufferMode(bool singleBufferMode);