blast: implicitly cache buffers

Sending buffers between processes is expensive so they must be
cached. Currently buffers are explictly cached. This patch
updates them to implicitly cache them per client process.
The max buffers per process is 64. After that, the cache
will try to evict buffers that have been destroyed. If it doesn't
find any, it will evict the least recently used buffer.

Test: Transaction_test
Bug: 80477568

Change-Id: Id048d32e05e19d0ce875212cbcddf997736110fd
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index bef68ef..fd30e3b 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -722,42 +722,6 @@
         return error;
     }
 
-    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
-                                 int32_t* outBufferId) {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-
-        data.writeStrongBinder(token);
-        if (buffer) {
-            data.writeBool(true);
-            data.write(*buffer);
-        } else {
-            data.writeBool(false);
-        }
-
-        status_t result = remote()->transact(BnSurfaceComposer::CACHE_BUFFER, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-
-        int32_t id = -1;
-        result = reply.readInt32(&id);
-        if (result == NO_ERROR) {
-            *outBufferId = id;
-        }
-        return result;
-    }
-
-    virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-
-        data.writeStrongBinder(token);
-        data.writeInt32(bufferId);
-
-        return remote()->transact(BnSurfaceComposer::UNCACHE_BUFFER, data, &reply);
-    }
-
     virtual status_t isWideColorDisplay(const sp<IBinder>& token,
                                         bool* outIsWideColorDisplay) const {
         Parcel data, reply;
@@ -1238,48 +1202,6 @@
             }
             return error;
         }
-        case CACHE_BUFFER: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> token;
-            status_t result = data.readStrongBinder(&token);
-            if (result != NO_ERROR) {
-                ALOGE("cache buffer failure in reading token: %d", result);
-                return result;
-            }
-
-            sp<GraphicBuffer> buffer = new GraphicBuffer();
-            if (data.readBool()) {
-                result = data.read(*buffer);
-                if (result != NO_ERROR) {
-                    ALOGE("cache buffer failure in reading buffer: %d", result);
-                    return result;
-                }
-            }
-            int32_t bufferId = -1;
-            status_t error = cacheBuffer(token, buffer, &bufferId);
-            if (error == NO_ERROR) {
-                reply->writeInt32(bufferId);
-            }
-            return error;
-        }
-        case UNCACHE_BUFFER: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> token;
-            status_t result = data.readStrongBinder(&token);
-            if (result != NO_ERROR) {
-                ALOGE("uncache buffer failure in reading token: %d", result);
-                return result;
-            }
-
-            int32_t bufferId = -1;
-            result = data.readInt32(&bufferId);
-            if (result != NO_ERROR) {
-                ALOGE("uncache buffer failure in reading buffer id: %d", result);
-                return result;
-            }
-
-            return uncacheBuffer(token, bufferId);
-        }
         case IS_WIDE_COLOR_DISPLAY: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = nullptr;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c712bde..da624cc 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -48,6 +48,9 @@
 
 #include <private/gui/ComposerService.h>
 
+// This server size should always be smaller than the server cache size
+#define BUFFER_CACHE_MAX_SIZE 64
+
 namespace android {
 
 using ui::ColorMode;
@@ -230,6 +233,113 @@
 
 // ---------------------------------------------------------------------------
 
+class BufferCache : public Singleton<BufferCache> {
+public:
+    BufferCache() : token(new BBinder()) {}
+
+    sp<IBinder> getToken() {
+        return IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    }
+
+    int32_t getId(const sp<GraphicBuffer>& buffer) {
+        std::lock_guard lock(mMutex);
+
+        auto itr = mBuffers.find(buffer);
+        if (itr == mBuffers.end()) {
+            return -1;
+        }
+        itr->second.counter = getCounter();
+        return itr->second.id;
+    }
+
+    int32_t cache(const sp<GraphicBuffer>& buffer) {
+        std::lock_guard lock(mMutex);
+
+        int32_t bufferId = getNextAvailableId();
+
+        mBuffers[buffer].id = bufferId;
+        mBuffers[buffer].counter = getCounter();
+        return bufferId;
+    }
+
+private:
+    int32_t evictDestroyedBuffer() REQUIRES(mMutex) {
+        auto itr = mBuffers.begin();
+        while (itr != mBuffers.end()) {
+            auto& buffer = itr->first;
+            if (buffer == nullptr || buffer.promote() == nullptr) {
+                int32_t bufferId = itr->second.id;
+                mBuffers.erase(itr);
+                return bufferId;
+            }
+            itr++;
+        }
+        return -1;
+    }
+
+    int32_t evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
+        if (mBuffers.size() < 0) {
+            return -1;
+        }
+        auto itr = mBuffers.begin();
+        uint64_t minCounter = itr->second.counter;
+        auto minBuffer = itr;
+        itr++;
+
+        while (itr != mBuffers.end()) {
+            uint64_t counter = itr->second.counter;
+            if (counter < minCounter) {
+                minCounter = counter;
+                minBuffer = itr;
+            }
+            itr++;
+        }
+        int32_t minBufferId = minBuffer->second.id;
+        mBuffers.erase(minBuffer);
+        return minBufferId;
+    }
+
+    int32_t getNextAvailableId() REQUIRES(mMutex) {
+        static int32_t id = 0;
+        if (id + 1 < BUFFER_CACHE_MAX_SIZE) {
+            return id++;
+        }
+
+        // There are no more valid cache ids. To set additional buffers, evict existing buffers
+        // and reuse their cache ids.
+        int32_t bufferId = evictDestroyedBuffer();
+        if (bufferId > 0) {
+            return bufferId;
+        }
+        return evictLeastRecentlyUsedBuffer();
+    }
+
+    uint64_t getCounter() REQUIRES(mMutex) {
+        static uint64_t counter = 0;
+        return counter++;
+    }
+
+    struct Metadata {
+        // The cache id of a buffer that can be set to ISurfaceComposer. When ISurfaceComposer
+        // recieves this id, it can retrieve the buffer from its cache. Caching GraphicBuffers
+        // is important because sending them across processes is expensive.
+        int32_t id = 0;
+        // When a buffer is set, a counter is incremented and stored in the cache's metadata.
+        // When an buffer must be evicted, the entry with the lowest counter value is chosen.
+        uint64_t counter = 0;
+    };
+
+    std::mutex mMutex;
+    std::map<wp<GraphicBuffer>, Metadata> mBuffers GUARDED_BY(mMutex);
+
+    // Used by ISurfaceComposer to identify which process is sending the cached buffer.
+    sp<IBinder> token;
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BufferCache);
+
+// ---------------------------------------------------------------------------
+
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
       : mForceSynchronous(other.mForceSynchronous),
         mTransactionNestCount(other.mTransactionNestCount),
@@ -772,22 +882,17 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eBufferChanged;
-    s->buffer = buffer;
 
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
+    int32_t bufferId = BufferCache::getInstance().getId(buffer);
+    if (bufferId < 0) {
+        bufferId = BufferCache::getInstance().cache(buffer);
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachedBuffer(
-        const sp<SurfaceControl>& sc, int32_t bufferId) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
+        s->what |= layer_state_t::eBufferChanged;
+        s->buffer = buffer;
     }
+
     s->what |= layer_state_t::eCachedBufferChanged;
-    s->cachedBuffer.token = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    s->cachedBuffer.token = BufferCache::getInstance().getToken();
     s->cachedBuffer.bufferId = bufferId;
 
     registerSurfaceControlForCallback(sc);
@@ -1230,26 +1335,6 @@
 
 // ----------------------------------------------------------------------------
 
-status_t SurfaceComposerClient::cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId) {
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    if (buffer == nullptr || outBufferId == nullptr) {
-        return BAD_VALUE;
-    }
-    return sf->cacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
-                           buffer, outBufferId);
-}
-
-status_t SurfaceComposerClient::uncacheBuffer(int32_t bufferId) {
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    if (bufferId < 0) {
-        return BAD_VALUE;
-    }
-    return sf->uncacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
-                             bufferId);
-}
-
-// ----------------------------------------------------------------------------
-
 status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     return sf->enableVSyncInjections(enable);
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3899f6a..7146b7d 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -320,11 +320,6 @@
      */
     virtual status_t getProtectedContentSupport(bool* outSupported) const = 0;
 
-    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
-                                 int32_t* outBufferId) = 0;
-
-    virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) = 0;
-
     /*
      * Queries whether the given display is a wide color display.
      * Requires the ACCESS_SURFACE_FLINGER permission.
@@ -373,8 +368,6 @@
         SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
         GET_DISPLAYED_CONTENT_SAMPLE,
         GET_PROTECTED_CONTENT_SUPPORT,
-        CACHE_BUFFER,
-        UNCACHE_BUFFER,
         IS_WIDE_COLOR_DISPLAY,
         GET_DISPLAY_NATIVE_PRIMARIES,
         // Always append new enum to the end.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8e2bb2b..28e04ca 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -152,12 +152,6 @@
     static void doDropReferenceTransaction(const sp<IBinder>& handle,
             const sp<ISurfaceComposerClient>& client);
 
-    // Caches a buffer with the ISurfaceComposer so the buffer does not need to be resent across
-    // processes
-    static status_t cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId);
-    // Uncaches a buffer set by cacheBuffer
-    static status_t uncacheBuffer(int32_t bufferId);
-
     // Queries whether a given display is wide color display.
     static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay);
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 1705fd7..8bd589d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -664,11 +664,6 @@
     status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; }
     status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; }
 
-    status_t cacheBuffer(const sp<IBinder>& /*token*/, const sp<GraphicBuffer>& /*buffer*/,
-                         int32_t* /*outBufferId*/) {
-        return NO_ERROR;
-    }
-    status_t uncacheBuffer(const sp<IBinder>& /*token*/, int32_t /*bufferId*/) { return NO_ERROR; }
     status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; }
 
 protected: