SharedBufferStack now can grow up to 16 buffers.

there is a new resize() api, which currently only allows growing.

Change-Id: Ia37b81b73be466d2491ffed7f3a23cd8e113c6fe
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 2504d39..c23832d 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -129,7 +129,7 @@
 
 // ----------------------------------------------------------------------------
 
-// 4 KB max
+// 32 KB max
 class SharedClient
 {
 public:
@@ -166,7 +166,7 @@
 protected:
     SharedClient* const mSharedClient;
     SharedBufferStack* const mSharedStack;
-    const int mNumBuffers;
+    int mNumBuffers;
     const int mIdentity;
     int32_t computeTail() const;
 
@@ -217,6 +217,7 @@
     bool needNewBuffer(int buffer) const;
     status_t setDirtyRegion(int buffer, const Region& reg);
     status_t setCrop(int buffer, const Rect& reg);
+    status_t setBufferCount(int bufferCount);
     
 private:
     friend struct Condition;
@@ -269,13 +270,61 @@
     status_t reallocate();
     status_t assertReallocate(int buffer);
     int32_t getQueuedCount() const;
-    
     Region getDirtyRegion(int buffer) const;
 
+    status_t resize(int newNumBuffers);
+
     SharedBufferStack::Statistics getStats() const;
     
 
 private:
+    /*
+     * BufferList is basically a fixed-capacity sorted-vector of
+     * unsigned 5-bits ints using a 32-bits int as storage.
+     * it has efficient iterators to find items in the list and not in the list.
+     */
+    class BufferList {
+        size_t mCapacity;
+        uint32_t mList;
+    public:
+        BufferList(size_t c = NUM_BUFFER_MAX) : mCapacity(c), mList(0) { }
+        status_t add(int value);
+        status_t remove(int value);
+
+        class const_iterator {
+            friend class BufferList;
+            uint32_t mask, curr;
+            const_iterator(uint32_t mask) :
+                mask(mask), curr(31 - __builtin_clz(mask)) { }
+        public:
+            inline bool operator == (const const_iterator& rhs) const {
+                return mask == rhs.mask;
+            }
+            inline bool operator != (const const_iterator& rhs) const {
+                return mask != rhs.mask;
+            }
+            inline int operator *() const { return curr; }
+            inline const const_iterator& operator ++(int) {
+                mask &= ~curr;
+                curr = 31 - __builtin_clz(mask);
+                return *this;
+            }
+        };
+
+        inline const_iterator begin() const {
+            return const_iterator(mList);
+        }
+        inline const_iterator end() const   {
+            return const_iterator(0);
+        }
+        inline const_iterator free_begin() const {
+            uint32_t mask = (1 << (32-mCapacity)) - 1;
+            return const_iterator( ~(mList | mask) );
+        }
+    };
+
+    BufferList mBufferList;
+
     struct UnlockUpdate : public UpdateBase {
         const int lockedBuffer;
         inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer);
diff --git a/include/surfaceflinger/ISurface.h b/include/surfaceflinger/ISurface.h
index 472f759..9476686 100644
--- a/include/surfaceflinger/ISurface.h
+++ b/include/surfaceflinger/ISurface.h
@@ -47,12 +47,14 @@
         POST_BUFFER, // one-way transaction
         CREATE_OVERLAY,
         REQUEST_BUFFER,
+        SET_BUFFER_COUNT,
     };
 
 public: 
     DECLARE_META_INTERFACE(Surface);
 
     virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) = 0; 
+    virtual status_t setBufferCount(int bufferCount) = 0;
     
     class BufferHeap {
     public:
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 7ab3a00..973780f 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -217,6 +217,7 @@
     int  connect(int api);
     int  disconnect(int api);
     int  crop(Rect const* rect);
+    int  setBufferCount(int bufferCount);
 
     /*
      *  private stuff...
diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp
index 71573ac..1fe997d 100644
--- a/libs/surfaceflinger/Layer.cpp
+++ b/libs/surfaceflinger/Layer.cpp
@@ -208,6 +208,30 @@
     drawWithOpenGL(clip, tex);
 }
 
+
+status_t Layer::setBufferCount(int bufferCount)
+{
+    // this ensures our client doesn't go away while we're accessing
+    // the shared area.
+    sp<Client> ourClient(client.promote());
+    if (ourClient == 0) {
+        // oops, the client is already gone
+        return DEAD_OBJECT;
+    }
+
+    status_t err;
+
+    // FIXME: resize() below is NOT thread-safe, we need to synchronize
+    // the users of lcblk in our process (ie: retire), and we assume the
+    // client is not mucking with the SharedStack, which is only enforced
+    // by construction, therefore we need to protect ourselves against
+    // buggy and malicious client (as always)
+
+    err = lcblk->resize(bufferCount);
+
+    return err;
+}
+
 sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
 {
     sp<GraphicBuffer> buffer;
@@ -642,6 +666,16 @@
     return buffer;
 }
 
+status_t Layer::SurfaceLayer::setBufferCount(int bufferCount)
+{
+    status_t err = DEAD_OBJECT;
+    sp<Layer> owner(getOwner());
+    if (owner != 0) {
+        err = owner->setBufferCount(bufferCount);
+    }
+    return err;
+}
+
 // ---------------------------------------------------------------------------
 
 
diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h
index 26fb6c6..80fbd6a 100644
--- a/libs/surfaceflinger/Layer.h
+++ b/libs/surfaceflinger/Layer.h
@@ -89,6 +89,7 @@
     uint32_t getEffectiveUsage(uint32_t usage) const;
 
     sp<GraphicBuffer> requestBuffer(int index, int usage);
+    status_t setBufferCount(int bufferCount);
     void destroy();
 
     class SurfaceLayer : public LayerBaseClient::Surface {
@@ -98,6 +99,7 @@
         ~SurfaceLayer();
     private:
         virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+        virtual status_t setBufferCount(int bufferCount);
         sp<Layer> getOwner() const {
             return static_cast<Layer*>(Surface::getOwner().get());
         }
diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
index 7d8d43f..63b9520 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/libs/surfaceflinger/LayerBase.cpp
@@ -621,6 +621,11 @@
     return NULL; 
 }
 
+status_t LayerBaseClient::Surface::setBufferCount(int bufferCount)
+{
+    return INVALID_OPERATION;
+}
+
 status_t LayerBaseClient::Surface::registerBuffers(
         const ISurface::BufferHeap& buffers) 
 { 
diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
index c500dcf..53b848f 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.h
@@ -302,6 +302,8 @@
 
     private:
         virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+        virtual status_t setBufferCount(int bufferCount);
+
         virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); 
         virtual void postBuffer(ssize_t offset);
         virtual void unregisterBuffers();
diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp
index bb86199..c5d0c0e 100644
--- a/libs/surfaceflinger_client/ISurface.cpp
+++ b/libs/surfaceflinger_client/ISurface.cpp
@@ -83,6 +83,16 @@
         return buffer;
     }
 
+    virtual status_t setBufferCount(int bufferCount)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
+        data.writeInt32(bufferCount);
+        remote()->transact(SET_BUFFER_COUNT, data, &reply);
+        status_t err = reply.readInt32();
+        return err;
+    }
+
     virtual status_t registerBuffers(const BufferHeap& buffers)
     {
         Parcel data, reply;
@@ -146,6 +156,13 @@
                 return BAD_VALUE;
             return reply->write(*buffer);
         }
+        case SET_BUFFER_COUNT: {
+            CHECK_INTERFACE(ISurface, data, reply);
+            int bufferCount = data.readInt32();
+            status_t err = setBufferCount(bufferCount);
+            reply->writeInt32(err);
+            return NO_ERROR;
+        }
         case REGISTER_BUFFERS: {
             CHECK_INTERFACE(ISurface, data, reply);
             BufferHeap buffer;
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index d6b2375..4a98026 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -305,7 +305,6 @@
     : UpdateBase(sbb), numBuffers(numBuffers) {
 }
 ssize_t SharedBufferServer::RetireUpdate::operator()() {
-    // head is only written in this function, which is single-thread.
     int32_t head = stack.head;
     if (uint32_t(head) >= NUM_BUFFER_MAX)
         return BAD_VALUE;
@@ -322,16 +321,14 @@
         }
     } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));
     
-    // update the head pointer
-    head = ((head+1 >= numBuffers) ? 0 : head+1);
-
     // lock the buffer before advancing head, which automatically unlocks
     // the buffer we preventively locked upon entering this function
+    head = (head + 1) % numBuffers;
     android_atomic_write(stack.index[head], &stack.inUse);
 
-    // advance head
+    // head is only modified here, so we don't need to use cmpxchg
     android_atomic_write(head, &stack.head);
-    
+
     // now that head has moved, we can increment the number of available buffers
     android_atomic_inc(&stack.available);
     return head;
@@ -450,6 +447,14 @@
     return stack.setDirtyRegion(buf, reg);
 }
 
+status_t SharedBufferClient::setBufferCount(int bufferCount)
+{
+    if (uint32_t(bufferCount) >= NUM_BUFFER_MAX)
+        return BAD_VALUE;
+    mNumBuffers = bufferCount;
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
@@ -463,6 +468,7 @@
     mSharedStack->reallocMask = 0;
     memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers));
     for (int i=0 ; i<num ; i++) {
+        mBufferList.add(i);
         mSharedStack->index[i] = i;
     }
 }
@@ -525,12 +531,85 @@
     return stack.getDirtyRegion(buf);
 }
 
+
+/*
+ *
+ * NOTE: this is not thread-safe on the server-side, meaning
+ * 'head' cannot move during this operation. The client-side
+ * can safely operate an usual.
+ *
+ */
+status_t SharedBufferServer::resize(int newNumBuffers)
+{
+    if (uint32_t(newNumBuffers) >= NUM_BUFFER_MAX)
+        return BAD_VALUE;
+
+    // for now we're not supporting shrinking
+    const int numBuffers = mNumBuffers;
+    if (newNumBuffers < numBuffers)
+        return BAD_VALUE;
+
+    SharedBufferStack& stack( *mSharedStack );
+    const int extra = newNumBuffers - numBuffers;
+
+    // read the head, make sure it's valid
+    int32_t head = stack.head;
+    if (uint32_t(head) >= NUM_BUFFER_MAX)
+        return BAD_VALUE;
+
+    int base = numBuffers;
+    int32_t avail = stack.available;
+    int tail = head - avail + 1;
+    if (tail >= 0) {
+        int8_t* const index = const_cast<int8_t*>(stack.index);
+        const int nb = numBuffers - head;
+        memmove(&index[head + extra], &index[head], nb);
+        base = head;
+        // move head 'extra' ahead, this doesn't impact stack.index[head];
+        stack.head = head + extra;
+    }
+    stack.available += extra;
+
+    // fill the new free space with unused buffers
+    BufferList::const_iterator curr(mBufferList.free_begin());
+    for (int i=0 ; i<extra ; i++) {
+        stack.index[base+i] = *curr++;
+        mBufferList.add(stack.index[base+i]);
+    }
+
+    mNumBuffers = newNumBuffers;
+    return NO_ERROR;
+}
+
 SharedBufferStack::Statistics SharedBufferServer::getStats() const
 {
     SharedBufferStack& stack( *mSharedStack );
     return stack.stats;
 }
 
+// ---------------------------------------------------------------------------
+status_t SharedBufferServer::BufferList::add(int value)
+{
+    if (uint32_t(value) >= mCapacity)
+        return BAD_VALUE;
+    uint32_t mask = 1<<(31-value);
+    if (mList & mask)
+        return ALREADY_EXISTS;
+    mList |= mask;
+    return NO_ERROR;
+}
+
+status_t SharedBufferServer::BufferList::remove(int value)
+{
+    if (uint32_t(value) >= mCapacity)
+        return BAD_VALUE;
+    uint32_t mask = 1<<(31-value);
+    if (!(mList & mask))
+        return NAME_NOT_FOUND;
+    mList &= ~mask;
+    return NO_ERROR;
+}
+
 
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index eee4dae..afbeafb 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -673,6 +673,27 @@
     return NO_ERROR;
 }
 
+int Surface::setBufferCount(int bufferCount)
+{
+    sp<ISurface> s(mSurface);
+    if (s == 0) return NO_INIT;
+
+    // FIXME: this needs to be synchronized dequeue/queue
+
+    status_t err = s->setBufferCount(bufferCount);
+    LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s",
+            bufferCount, strerror(-err));
+    if (err == NO_ERROR) {
+        err = mSharedBufferClient->getStatus();
+        LOGE_IF(err,  "Surface (identity=%d) state = %d", mIdentity, err);
+        if (!err) {
+            // update our local copy of the buffer count
+            mSharedBufferClient->setBufferCount(bufferCount);
+        }
+    }
+    return err;
+}
+
 
 // ----------------------------------------------------------------------------