Add interface for controlling single buffer auto refresh

- Adds a boolean to BufferQueue that controls whether or not auto
  refresh is enabled in SurfaceFlinger when in single buffer mode.
- Adds plumbing up to ANativeWindow.
- When enabled, it will cache the shared buffer slot in Surface in
  order to prevent the Binder transaction with SurfaceFlinger.

Bug 24940410

Change-Id: I83142afdc00e203f198a32288f071d926f8fda95
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 036ef1e..5e3924a 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,7 +38,7 @@
     mAcquireCalled(false),
     mTransformToDisplayInverse(false),
     mSurfaceDamage(),
-    mSingleBufferMode(false),
+    mAutoRefresh(false),
     mQueuedBuffer(true),
     mIsStale(false) {
 }
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 92285e5..f8b50cc 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -67,7 +67,7 @@
         }
 
         bool sharedBufferAvailable = mCore->mSingleBufferMode &&
-                mCore->mSingleBufferSlot !=
+                mCore->mAutoRefresh && mCore->mSingleBufferSlot !=
                 BufferQueueCore::INVALID_BUFFER_SLOT;
 
         // In asynchronous mode the list is guaranteed to be one buffer deep,
@@ -214,16 +214,15 @@
                     (mCore->mSingleBufferCache.transform &
                     NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
             outBuffer->mSurfaceDamage = Region::INVALID_REGION;
-            outBuffer->mSingleBufferMode = true;
             outBuffer->mQueuedBuffer = false;
             outBuffer->mIsStale = false;
+            outBuffer->mAutoRefresh = mCore->mSingleBufferMode &&
+                    mCore->mAutoRefresh;
         } else {
             slot = front->mSlot;
             *outBuffer = *front;
         }
 
-        outBuffer->mSingleBufferMode = mCore->mSingleBufferMode;
-
         ATRACE_BUFFER_INDEX(slot);
 
         BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index f785db0..ba07362 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -79,6 +79,7 @@
     mGenerationNumber(0),
     mAsyncMode(false),
     mSingleBufferMode(false),
+    mAutoRefresh(false),
     mSingleBufferSlot(INVALID_BUFFER_SLOT),
     mSingleBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
             HAL_DATASPACE_UNKNOWN)
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9d42464..e065e61 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -806,8 +806,8 @@
                 mCore->mDequeueBufferCannotBlock ||
                 (mCore->mSingleBufferMode && mCore->mSingleBufferSlot == slot);
         item.mSurfaceDamage = surfaceDamage;
-        item.mSingleBufferMode = mCore->mSingleBufferMode;
         item.mQueuedBuffer = true;
+        item.mAutoRefresh = mCore->mSingleBufferMode && mCore->mAutoRefresh;
 
         mStickyTransform = stickyTransform;
 
@@ -1309,6 +1309,16 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::setAutoRefresh(bool autoRefresh) {
+    ATRACE_CALL();
+    BQ_LOGV("setAutoRefresh: %d", autoRefresh);
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    mCore->mAutoRefresh = autoRefresh;
+    return NO_ERROR;
+}
+
 status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) {
     ATRACE_CALL();
     BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index e1abd45..55059dd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -407,7 +407,7 @@
     }
 
     // Do whatever sync ops we need to do before releasing the old slot.
-    if (!item.mSingleBufferMode || slot != mCurrentTexture) {
+    if (slot != mCurrentTexture) {
         err = syncForReleaseLocked(mEglDisplay);
         if (err != NO_ERROR) {
             // Release the buffer we just acquired.  It's not safe to
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 2478601..c66694d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -52,6 +52,7 @@
     SET_ASYNC_MODE,
     GET_NEXT_FRAME_NUMBER,
     SET_SINGLE_BUFFER_MODE,
+    SET_AUTO_REFRESH,
     SET_DEQUEUE_TIMEOUT,
 };
 
@@ -355,6 +356,18 @@
         return result;
     }
 
+    virtual status_t setAutoRefresh(bool autoRefresh) {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32(autoRefresh);
+        status_t result = remote()->transact(SET_AUTO_REFRESH, data, &reply);
+        if (result == NO_ERROR) {
+            result = reply.readInt32();
+        }
+        return result;
+    }
+
     virtual status_t setDequeueTimeout(nsecs_t timeout) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -562,6 +575,13 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case SET_AUTO_REFRESH: {
+            CHECK_INTERFACE(IGraphicBuffer, data, reply);
+            bool autoRefresh = data.readInt32();
+            status_t result = setAutoRefresh(autoRefresh);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
         case SET_DEQUEUE_TIMEOUT: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             nsecs_t timeout = data.readInt64();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6fc55c3..42adf90 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -44,7 +44,11 @@
         bool controlledByApp)
     : mGraphicBufferProducer(bufferProducer),
       mCrop(Rect::EMPTY_RECT),
-      mGenerationNumber(0)
+      mGenerationNumber(0),
+      mSingleBufferMode(false),
+      mAutoRefresh(false),
+      mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
+      mSharedBufferHasBeenQueued(false)
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -232,6 +236,16 @@
 
         reqFormat = mReqFormat;
         reqUsage = mReqUsage;
+
+        if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot !=
+                BufferItem::INVALID_BUFFER_SLOT) {
+            sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
+            if (gbuf != NULL) {
+                *buffer = gbuf.get();
+                *fenceFd = -1;
+                return OK;
+            }
+        }
     } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer
 
     int buf = -1;
@@ -279,6 +293,15 @@
     }
 
     *buffer = gbuf.get();
+
+    if (mSingleBufferMode && mAutoRefresh) {
+        mSharedBufferSlot = buf;
+        mSharedBufferHasBeenQueued = false;
+    } else if (mSharedBufferSlot == buf) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+        mSharedBufferHasBeenQueued = false;
+    }
+
     return OK;
 }
 
@@ -294,8 +317,19 @@
         }
         return i;
     }
+    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
     sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
     mGraphicBufferProducer->cancelBuffer(i, fence);
+
+    if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+        mSharedBufferHasBeenQueued = true;
+    }
+
     return OK;
 }
 
@@ -323,6 +357,7 @@
     Mutex::Autolock lock(mMutex);
     int64_t timestamp;
     bool isAutoTimestamp = false;
+
     if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
         timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         isAutoTimestamp = true;
@@ -338,6 +373,12 @@
         }
         return i;
     }
+    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
 
 
     // Make sure the crop rectangle is entirely inside the buffer.
@@ -417,6 +458,7 @@
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
     }
+
     uint32_t numPendingBuffers = 0;
     uint32_t hint = 0;
     output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
@@ -434,6 +476,10 @@
         mDirtyRegion = Region::INVALID_REGION;
     }
 
+    if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+        mSharedBufferHasBeenQueued = true;
+    }
+
     return err;
 }
 
@@ -557,6 +603,9 @@
     case NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE:
         res = dispatchSetSingleBufferMode(args);
         break;
+    case NATIVE_WINDOW_SET_AUTO_REFRESH:
+        res = dispatchSetAutoRefresh(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -669,8 +718,12 @@
 
 int Surface::dispatchSetSingleBufferMode(va_list args) {
     bool singleBufferMode = va_arg(args, int);
-    setSingleBufferMode(singleBufferMode);
-    return NO_ERROR;
+    return setSingleBufferMode(singleBufferMode);
+}
+
+int Surface::dispatchSetAutoRefresh(va_list args) {
+    bool autoRefresh = va_arg(args, int);
+    return setAutoRefresh(autoRefresh);
 }
 
 int Surface::connect(int api) {
@@ -714,6 +767,8 @@
     ATRACE_CALL();
     ALOGV("Surface::disconnect");
     Mutex::Autolock lock(mMutex);
+    mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    mSharedBufferHasBeenQueued = false;
     freeAllBuffers();
     int err = mGraphicBufferProducer->disconnect(api);
     if (!err) {
@@ -796,6 +851,9 @@
 {
     ALOGV("Surface::setUsage");
     Mutex::Autolock lock(mMutex);
+    if (reqUsage != mReqUsage) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqUsage = reqUsage;
     return OK;
 }
@@ -888,12 +946,29 @@
 
     status_t err = mGraphicBufferProducer->setSingleBufferMode(
             singleBufferMode);
-    ALOGE_IF(err, "IGraphicsBufferProducer::setSingleBufferMode(%d) returned"
+    if (err == NO_ERROR) {
+        mSingleBufferMode = singleBufferMode;
+    }
+    ALOGE_IF(err, "IGraphicBufferProducer::setSingleBufferMode(%d) returned"
             "%s", singleBufferMode, strerror(-err));
 
     return err;
 }
 
+int Surface::setAutoRefresh(bool autoRefresh) {
+    ATRACE_CALL();
+    ALOGV("Surface::setAutoRefresh (%d)", autoRefresh);
+    Mutex::Autolock lock(mMutex);
+
+    status_t err = mGraphicBufferProducer->setAutoRefresh(autoRefresh);
+    if (err == NO_ERROR) {
+        mAutoRefresh = autoRefresh;
+    }
+    ALOGE_IF(err, "IGraphicBufferProducer::setAutoRefresh(%d) returned %s",
+            autoRefresh, strerror(-err));
+    return err;
+}
+
 int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
 {
     ATRACE_CALL();
@@ -903,6 +978,9 @@
         return BAD_VALUE;
 
     Mutex::Autolock lock(mMutex);
+    if (width != mReqWidth || height != mReqHeight) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqWidth = width;
     mReqHeight = height;
     return NO_ERROR;
@@ -917,6 +995,9 @@
         return BAD_VALUE;
 
     Mutex::Autolock lock(mMutex);
+    if (width != mUserWidth || height != mUserHeight) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mUserWidth = width;
     mUserHeight = height;
     return NO_ERROR;
@@ -927,6 +1008,9 @@
     ALOGV("Surface::setBuffersFormat");
 
     Mutex::Autolock lock(mMutex);
+    if (format != mReqFormat) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqFormat = format;
     return NO_ERROR;
 }
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index f4c47ed..b6af166 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -65,6 +65,27 @@
         BufferQueue::createBufferQueue(&mProducer, &mConsumer);
     }
 
+    void testBufferItem(const IGraphicBufferProducer::QueueBufferInput& input,
+            const BufferItem& item) {
+        int64_t timestamp;
+        bool isAutoTimestamp;
+        android_dataspace dataSpace;
+        Rect crop;
+        int scalingMode;
+        uint32_t transform;
+        sp<Fence> fence;
+
+        input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop,
+                &scalingMode, &transform, &fence, NULL);
+        ASSERT_EQ(timestamp, item.mTimestamp);
+        ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp);
+        ASSERT_EQ(dataSpace, item.mDataSpace);
+        ASSERT_EQ(crop, item.mCrop);
+        ASSERT_EQ(static_cast<uint32_t>(scalingMode), item.mScalingMode);
+        ASSERT_EQ(transform, item.mTransform);
+        ASSERT_EQ(fence, item.mFence);
+    }
+
     sp<IGraphicBufferProducer> mProducer;
     sp<IGraphicBufferConsumer> mConsumer;
 };
@@ -521,7 +542,7 @@
     ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer));
 }
 
-TEST_F(BufferQueueTest, TestSingleBufferMode) {
+TEST_F(BufferQueueTest, TestSingleBufferModeWithoutAutoRefresh) {
     createBufferQueue();
     sp<DummyConsumer> dc(new DummyConsumer);
     ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
@@ -545,19 +566,66 @@
             NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
     ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
 
+    // Repeatedly queue and dequeue a buffer from the producer side, it should
+    // always return the same one. And we won't run out of buffers because it's
+    // always the same one and because async mode gets enabled.
+    int slot;
+    for (int i = 0; i < 5; i++) {
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(singleSlot, slot);
+        ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+    }
+
+    // acquire the buffer
+    BufferItem item;
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(singleSlot, item.mSlot);
+    testBufferItem(input, item);
+    ASSERT_EQ(true, item.mQueuedBuffer);
+    ASSERT_EQ(false, item.mAutoRefresh);
+
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+    // attempt to acquire a second time should return no buffer available
+    ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
+            mConsumer->acquireBuffer(&item, 0));
+}
+
+TEST_F(BufferQueueTest, TestSingleBufferModeWithAutoRefresh) {
+    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));
+
+    ASSERT_EQ(OK, mProducer->setSingleBufferMode(true));
+    ASSERT_EQ(OK, mProducer->setAutoRefresh(true));
+
+    // Get a buffer
+    int singleSlot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer));
+
+    // Queue the buffer
+    IGraphicBufferProducer::QueueBufferInput input(0, false,
+            HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
+            NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+
     // Repeatedly acquire and release a buffer from the consumer side, it should
     // always return the same one.
     BufferItem item;
     for (int i = 0; i < 5; i++) {
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
         ASSERT_EQ(singleSlot, item.mSlot);
-        ASSERT_EQ(0, item.mTimestamp);
-        ASSERT_EQ(false, item.mIsAutoTimestamp);
-        ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
-        ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
-        ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
-        ASSERT_EQ(0u, item.mTransform);
-        ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+        testBufferItem(input, item);
+        ASSERT_EQ(i == 0, item.mQueuedBuffer);
+        ASSERT_EQ(true, item.mAutoRefresh);
 
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
@@ -585,6 +653,8 @@
         ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
         ASSERT_EQ(0u, item.mTransform);
         ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+        ASSERT_EQ(i == 0, item.mQueuedBuffer);
+        ASSERT_EQ(true, item.mAutoRefresh);
 
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));