OSDN Git Service

libgui: Add dequeue/attach timeout
authorDan Stoza <stoza@google.com>
Tue, 30 Jun 2015 20:43:32 +0000 (13:43 -0700)
committerDan Stoza <stoza@google.com>
Tue, 5 Jan 2016 23:02:32 +0000 (15:02 -0800)
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

include/gui/BufferQueueProducer.h
include/gui/IGraphicBufferProducer.h
include/gui/Surface.h
libs/gui/BufferQueueProducer.cpp
libs/gui/IGraphicBufferProducer.cpp
libs/gui/Surface.cpp
libs/gui/tests/BufferQueue_test.cpp
services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
services/surfaceflinger/MonitoredProducer.cpp
services/surfaceflinger/MonitoredProducer.h

index 5fe5ce0..835593f 100644 (file)
@@ -174,7 +174,10 @@ public:
     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 @@ private:
     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
index d6daca7..8646981 100644 (file)
@@ -177,6 +177,8 @@ public:
     // * 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 @@ public:
     // * 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 @@ public:
     // 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;
 };
 
 // ----------------------------------------------------------------------------
index bcc3fff..f79210b 100644 (file)
@@ -117,6 +117,9 @@ public:
      * in <system/window.h>. */
     int setScalingMode(int mode);
 
+    // See IGraphicBufferProducer::setDequeueTimeout
+    status_t setDequeueTimeout(nsecs_t timeout);
+
 protected:
     virtual ~Surface();
 
index 9271ed8..c48f237 100644 (file)
@@ -43,7 +43,8 @@ BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
     mCallbackMutex(),
     mNextCallbackTicket(0),
     mCurrentCallbackTicket(0),
-    mCallbackCondition() {}
+    mCallbackCondition(),
+    mDequeueTimeout(-1) {}
 
 BufferQueueProducer::~BufferQueueProducer() {}
 
@@ -271,7 +272,15 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
                     (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 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
     }
 
     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 @@ status_t BufferQueueProducer::setSingleBufferMode(bool singleBufferMode) {
         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;
 }
 
index d5310bd..0cca58d 100644 (file)
@@ -51,7 +51,8 @@ enum {
     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 @@ public:
         }
         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 @@ status_t BnGraphicBufferProducer::onTransact(
             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);
 }
index b816ff3..9e90ad0 100644 (file)
@@ -119,6 +119,10 @@ String8 Surface::getConsumerName() const {
     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);
index a32a90c..a45a5be 100644 (file)
@@ -535,4 +535,66 @@ TEST_F(BufferQueueTest, TestSingleBufferMode) {
     }
 }
 
+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
index f8fe8e1..64c1dd9 100644 (file)
@@ -551,6 +551,11 @@ status_t VirtualDisplaySurface::setSingleBufferMode(bool singleBufferMode) {
     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;
index bde7fcf..7f451a9 100644 (file)
@@ -120,7 +120,8 @@ private:
     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
index 16879ca..efc44ab 100644 (file)
@@ -135,6 +135,10 @@ status_t MonitoredProducer::setSingleBufferMode(bool singleBufferMode) {
     return mProducer->setSingleBufferMode(singleBufferMode);
 }
 
+status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
+    return mProducer->setDequeueTimeout(timeout);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return IInterface::asBinder(mProducer).get();
 }
index c2b2e24..aea2e39 100644 (file)
@@ -58,6 +58,7 @@ public:
     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);