Implement reducing the buffer count of a Surface.

Change-Id: I7f979c60c06d654aa8265002836277434bc1a64f
Bug: 3095167
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index d6ae5e9..9d589cf 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -285,6 +285,8 @@
     uint32_t getTransform(int buffer) const;
 
     status_t resize(int newNumBuffers);
+    status_t grow(int newNumBuffers);
+    status_t shrink(int newNumBuffers);
 
     SharedBufferStack::Statistics getStats() const;
     
@@ -346,6 +348,14 @@
     int mNumBuffers;
     BufferList mBufferList;
 
+    struct BuffersAvailableCondition : public ConditionBase {
+        int mNumBuffers;
+        inline BuffersAvailableCondition(SharedBufferServer* sbs,
+                int numBuffers);
+        inline bool operator()() const;
+        inline const char* name() const { return "BuffersAvailableCondition"; }
+    };
+
     struct UnlockUpdate : public UpdateBase {
         const int lockedBuffer;
         inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer);
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 8f583f0..3b2ef84 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -265,6 +265,14 @@
             (stack.queued > 0 && stack.inUse != buf));
 }
 
+SharedBufferServer::BuffersAvailableCondition::BuffersAvailableCondition(
+        SharedBufferServer* sbs, int numBuffers) : ConditionBase(sbs),
+        mNumBuffers(numBuffers) {
+}
+bool SharedBufferServer::BuffersAvailableCondition::operator()() const {
+    return stack.available == mNumBuffers;
+}
+
 // ----------------------------------------------------------------------------
 
 SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb)
@@ -448,6 +456,7 @@
 
     const nsecs_t now = systemTime(SYSTEM_TIME_THREAD);
     stack.stats.totalTime = ns2us(now - mDequeueTime[buf]);
+
     return err;
 }
 
@@ -492,6 +501,7 @@
     if (err == NO_ERROR) {
         mNumBuffers = bufferCount;
         queued_head = (stack.head + stack.queued) % mNumBuffers;
+        tail = computeTail();
     }
     return err;
 }
@@ -606,17 +616,24 @@
  */
 status_t SharedBufferServer::resize(int newNumBuffers)
 {
-    if (uint32_t(newNumBuffers) >= SharedBufferStack::NUM_BUFFER_MAX)
+    if ((unsigned int)(newNumBuffers) < SharedBufferStack::NUM_BUFFER_MIN ||
+        (unsigned int)(newNumBuffers) > SharedBufferStack::NUM_BUFFER_MAX) {
         return BAD_VALUE;
+    }
 
     RWLock::AutoWLock _l(mLock);
 
-    // for now we're not supporting shrinking
-    const int numBuffers = mNumBuffers;
-    if (newNumBuffers < numBuffers)
-        return BAD_VALUE;
+    if (newNumBuffers < mNumBuffers) {
+        return shrink(newNumBuffers);
+    } else {
+        return grow(newNumBuffers);
+    }
+}
 
+status_t SharedBufferServer::grow(int newNumBuffers)
+{
     SharedBufferStack& stack( *mSharedStack );
+    const int numBuffers = mNumBuffers;
     const int extra = newNumBuffers - numBuffers;
 
     // read the head, make sure it's valid
@@ -650,6 +667,54 @@
     return NO_ERROR;
 }
 
+status_t SharedBufferServer::shrink(int newNumBuffers)
+{
+    SharedBufferStack& stack( *mSharedStack );
+
+    // Shrinking is only supported if there are no buffers currently dequeued.
+    int32_t avail = stack.available;
+    int32_t queued = stack.queued;
+    if (avail + queued != mNumBuffers) {
+        return INVALID_OPERATION;
+    }
+
+    // Wait for any queued buffers to be displayed.
+    BuffersAvailableCondition condition(this, mNumBuffers);
+    status_t err = waitForCondition(condition);
+    if (err < 0) {
+        return err;
+    }
+
+    // Reset head to index 0 and make it refer to buffer 0.  The same renaming
+    // (head -> 0) is done in the BufferManager.
+    int32_t head = stack.head;
+    int8_t* index = const_cast<int8_t*>(stack.index);
+    for (int8_t i = 0; i < newNumBuffers; i++) {
+        index[i] = i;
+    }
+    stack.head = 0;
+    stack.headBuf = 0;
+
+    // If one of the buffers is in use it must be the head buffer, which we are
+    // renaming to buffer 0.
+    if (stack.inUse > 0) {
+        stack.inUse = 0;
+    }
+
+    // Free the buffers from the end of the list that are no longer needed.
+    for (int i = newNumBuffers; i < mNumBuffers; i++) {
+        mBufferList.remove(i);
+    }
+
+    // Tell the client to reallocate all the buffers.
+    reallocateAll();
+
+    mNumBuffers = newNumBuffers;
+    stack.available = newNumBuffers;
+
+    return NO_ERROR;
+}
+
 SharedBufferStack::Statistics SharedBufferServer::getStats() const
 {
     SharedBufferStack& stack( *mSharedStack );
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index c1e00ca..9467a4c 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -855,6 +855,12 @@
     status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc);
     LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s",
             bufferCount, strerror(-err));
+
+    if (err == NO_ERROR) {
+        // Clear out any references to the old buffers.
+        mBuffers.clear();
+    }
+
     return err;
 }
 
diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp
index f409f48..7ef5926 100644
--- a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp
+++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp
@@ -32,7 +32,8 @@
 int main(int argc, char** argv)
 {
     SharedClient client;
-    SharedBufferServer s(&client, 0, 4, 0);
+    sp<SharedBufferServer> ps(new SharedBufferServer(&client, 0, 4, 0));
+    SharedBufferServer& s(*ps);
     SharedBufferClient c(&client, 0, 4, 0);
 
     printf("basic test 0\n");
@@ -67,6 +68,10 @@
     int list3[6] = {3, 2, 1, 4, 5, 0};
     test0(s, c, 6, list3);
 
+    c.setBufferCount(4, resize);
+    int list4[4] = {1, 2, 3, 0};
+    test0(s, c, 4, list4);
+
     return 0;
 }
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1b06843..7be58c6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -340,8 +340,10 @@
 
     // NOTE: lcblk->resize() is protected by an internal lock
     status_t err = lcblk->resize(bufferCount);
-    if (err == NO_ERROR)
-        mBufferManager.resize(bufferCount);
+    if (err == NO_ERROR) {
+        EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+        mBufferManager.resize(bufferCount, mFlinger, dpy);
+    }
 
     return err;
 }
@@ -774,9 +776,52 @@
 {
 }
 
-status_t Layer::BufferManager::resize(size_t size)
+status_t Layer::BufferManager::resize(size_t size,
+        const sp<SurfaceFlinger>& flinger, EGLDisplay dpy)
 {
     Mutex::Autolock _l(mLock);
+
+    if (size < mNumBuffers) {
+        // Move the active texture into slot 0
+        BufferData activeBufferData = mBufferData[mActiveBuffer];
+        mBufferData[mActiveBuffer] = mBufferData[0];
+        mBufferData[0] = activeBufferData;
+        mActiveBuffer = 0;
+
+        // Free the buffers that are no longer needed.
+        for (size_t i = size; i < mNumBuffers; i++) {
+            mBufferData[i].buffer = 0;
+
+            // Create a message to destroy the textures on SurfaceFlinger's GL
+            // thread.
+            class MessageDestroyTexture : public MessageBase {
+                Image mTexture;
+                EGLDisplay mDpy;
+             public:
+                MessageDestroyTexture(const Image& texture, EGLDisplay dpy)
+                    : mTexture(texture), mDpy(dpy) { }
+                virtual bool handler() {
+                    status_t err = Layer::BufferManager::destroyTexture(
+                            &mTexture, mDpy);
+                    LOGE_IF(err<0, "error destroying texture: %d (%s)",
+                            mTexture.name, strerror(-err));
+                    return true; // XXX: err == 0;  ????
+                }
+            };
+
+            MessageDestroyTexture *msg = new MessageDestroyTexture(
+                    mBufferData[i].texture, dpy);
+
+            // Don't allow this texture to be cleaned up by
+            // BufferManager::destroy.
+            mBufferData[i].texture.name = -1U;
+            mBufferData[i].texture.image = EGL_NO_IMAGE_KHR;
+
+            // Post the message to the SurfaceFlinger object.
+            flinger->postMessageAsync(msg);
+        }
+    }
+
     mNumBuffers = size;
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index caa6d17..07434cf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -177,7 +177,8 @@
         sp<GraphicBuffer> detachBuffer(size_t index);
         status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer);
         // resize the number of active buffers
-        status_t resize(size_t size);
+        status_t resize(size_t size, const sp<SurfaceFlinger>& flinger,
+                EGLDisplay dpy);
 
         // ----------------------------------------------
         // must be called from GL thread