surfaceflinger: cache HWC client targets and buffers

Remember HWC client targets and buffers, and make sure we send each
unique slot/handle pair only once.  This allows the composer to
clone/register/retain each buffer only once.

Test: builds and boots
Change-Id: Ib485189043a9c132031e82d4d7380ace3bf9453d
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 1bd9616..6cb1af1 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -20,6 +20,7 @@
 #include <android/dvr/composer/1.0/IVrComposerClient.h>
 #include <inttypes.h>
 #include <log/log.h>
+#include <gui/BufferQueue.h>
 
 #include "ComposerHal.h"
 
@@ -221,9 +222,8 @@
 
 Error Composer::createLayer(Display display, Layer* outLayer)
 {
-    const uint32_t bufferSlotCount = 1;
     Error error = kDefaultError;
-    mClient->createLayer(display, bufferSlotCount,
+    mClient->createLayer(display, BufferQueue::NUM_BUFFER_SLOTS,
             [&](const auto& tmpError, const auto& tmpLayer) {
                 error = tmpError;
                 if (error != Error::NONE) {
@@ -427,12 +427,13 @@
     return unwrapRet(ret);
 }
 
-Error Composer::setClientTarget(Display display, const native_handle_t* target,
+Error Composer::setClientTarget(Display display, uint32_t slot,
+        const native_handle_t* target,
         int acquireFence, Dataspace dataspace,
         const std::vector<IComposerClient::Rect>& damage)
 {
     mWriter.selectDisplay(display);
-    mWriter.setClientTarget(0, target, acquireFence, dataspace, damage);
+    mWriter.setClientTarget(slot, target, acquireFence, dataspace, damage);
     return Error::NONE;
 }
 
@@ -472,7 +473,7 @@
 
 Error Composer::setClientTargetSlotCount(Display display)
 {
-    const uint32_t bufferSlotCount = 1;
+    const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
     auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
     return unwrapRet(ret);
 }
@@ -503,11 +504,11 @@
 }
 
 Error Composer::setLayerBuffer(Display display, Layer layer,
-        const native_handle_t* buffer, int acquireFence)
+        uint32_t slot, const native_handle_t* buffer, int acquireFence)
 {
     mWriter.selectDisplay(display);
     mWriter.selectLayer(layer);
-    mWriter.setLayerBuffer(0, buffer, acquireFence);
+    mWriter.setLayerBuffer(slot, buffer, acquireFence);
     return Error::NONE;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 329d787..1ede705 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -172,7 +172,14 @@
     Error presentDisplay(Display display, int* outPresentFence);
 
     Error setActiveConfig(Display display, Config config);
-    Error setClientTarget(Display display, const native_handle_t* target,
+
+    /*
+     * The composer caches client targets internally.  When target is nullptr,
+     * the composer uses slot to look up the client target from its cache.
+     * When target is not nullptr, the cache is updated with the new target.
+     */
+    Error setClientTarget(Display display, uint32_t slot,
+            const native_handle_t* target,
             int acquireFence, Dataspace dataspace,
             const std::vector<IComposerClient::Rect>& damage);
     Error setColorMode(Display display, ColorMode mode);
@@ -190,7 +197,8 @@
 
     Error setCursorPosition(Display display, Layer layer,
             int32_t x, int32_t y);
-    Error setLayerBuffer(Display display, Layer layer,
+    /* see setClientTarget for the purpose of slot */
+    Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
             const native_handle_t* buffer, int acquireFence);
     Error setLayerSurfaceDamage(Display display, Layer layer,
             const std::vector<IComposerClient::Rect>& damage);
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
index d801bb3..cb08f08 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
@@ -25,6 +25,7 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+class Fence;
 class IGraphicBufferProducer;
 class String8;
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 1998edf..d3d0d51 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -100,16 +100,18 @@
 
 status_t FramebufferSurface::advanceFrame() {
 #ifdef USE_HWC2
+    uint32_t slot = 0;
     sp<GraphicBuffer> buf;
     sp<Fence> acquireFence(Fence::NO_FENCE);
     android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
-    status_t result = nextBuffer(buf, acquireFence, dataspace);
+    status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
     if (result != NO_ERROR) {
         ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
                 strerror(-result), result);
         return result;
     }
-    result = mHwc.setClientTarget(mDisplayType, acquireFence, buf, dataspace);
+    result = mHwc.setClientTarget(mDisplayType, slot,
+            acquireFence, buf, dataspace);
     if (result != NO_ERROR) {
         ALOGE("error posting framebuffer: %d", result);
     }
@@ -123,8 +125,9 @@
 }
 
 #ifdef USE_HWC2
-status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer,
-        sp<Fence>& outFence, android_dataspace_t& outDataspace) {
+status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
+        sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
+        android_dataspace_t& outDataspace) {
 #else
 status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
 #endif
@@ -133,7 +136,12 @@
     BufferItem item;
     status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+#ifdef USE_HWC2
+        mHwcBufferCache->getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+                &outSlot, &outBuffer);
+#else
         outBuffer = mCurrentBuffer;
+#endif
         return NO_ERROR;
     } else if (err != NO_ERROR) {
         ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -169,9 +177,12 @@
     mCurrentFence = item.mFence;
 
     outFence = item.mFence;
-    outBuffer = mCurrentBuffer;
 #ifdef USE_HWC2
+    mHwcBufferCache->getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+            &outSlot, &outBuffer);
     outDataspace = item.mDataSpace;
+#else
+    outBuffer = mCurrentBuffer;
 #endif
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 439435a..5eea6b6 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -17,12 +17,14 @@
 #ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
 #define ANDROID_SF_FRAMEBUFFER_SURFACE_H
 
+#include "DisplaySurface.h"
+
 #include <stdint.h>
 #include <sys/types.h>
 
 #include <gui/ConsumerBase.h>
 
-#include "DisplaySurface.h"
+#include <memory>
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -31,6 +33,7 @@
 class Rect;
 class String8;
 class HWComposer;
+class HWComposerBufferCache;
 
 // ---------------------------------------------------------------------------
 
@@ -68,8 +71,8 @@
     // BufferQueue and releases the previously latched buffer to the
     // BufferQueue.  The new buffer is returned in the 'buffer' argument.
 #ifdef USE_HWC2
-    status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
-            android_dataspace_t& outDataspace);
+    status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
+            sp<Fence>& outFence, android_dataspace_t& outDataspace);
 #else
     status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence);
 #endif
@@ -93,6 +96,9 @@
     HWComposer& mHwc;
 
 #ifdef USE_HWC2
+    std::unique_ptr<HWComposerBufferCache> mHwcBufferCache =
+        std::make_unique<HWComposerBufferCache>();
+
     // Previous buffer to release after getting an updated retire fence
     bool mHasPendingRelease;
     int mPreviousBufferSlot;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 6ff5ea1..99a6d71 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -962,17 +962,18 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::setClientTarget(buffer_handle_t target,
+Error Display::setClientTarget(uint32_t slot, buffer_handle_t target,
         const sp<Fence>& acquireFence, android_dataspace_t dataspace)
 {
     // TODO: Properly encode client target surface damage
     int32_t fenceFd = acquireFence->dup();
 #ifdef BYPASS_IHWC
+    (void) slot;
     int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target,
             fenceFd, static_cast<int32_t>(dataspace), {0, nullptr});
 #else
-    auto intError = mDevice.mComposer->setClientTarget(mId, target, fenceFd,
-            static_cast<Hwc2::Dataspace>(dataspace),
+    auto intError = mDevice.mComposer->setClientTarget(mId, slot, target,
+            fenceFd, static_cast<Hwc2::Dataspace>(dataspace),
             std::vector<Hwc2::IComposerClient::Rect>());
 #endif
     return static_cast<Error>(intError);
@@ -1195,16 +1196,17 @@
     return static_cast<Error>(intError);
 }
 
-Error Layer::setBuffer(buffer_handle_t buffer,
+Error Layer::setBuffer(uint32_t slot, buffer_handle_t buffer,
         const sp<Fence>& acquireFence)
 {
     int32_t fenceFd = acquireFence->dup();
 #ifdef BYPASS_IHWC
+    (void) slot;
     int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
             mId, buffer, fenceFd);
 #else
     auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId,
-            mId, buffer, fenceFd);
+            mId, slot, buffer, fenceFd);
 #endif
     return static_cast<Error>(intError);
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 256b05d..d770f22 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -322,7 +322,7 @@
     [[clang::warn_unused_result]] Error setActiveConfig(
             const std::shared_ptr<const Config>& config);
     [[clang::warn_unused_result]] Error setClientTarget(
-            buffer_handle_t target,
+            uint32_t slot, buffer_handle_t target,
             const android::sp<android::Fence>& acquireFence,
             android_dataspace_t dataspace);
     [[clang::warn_unused_result]] Error setColorMode(android_color_mode_t mode);
@@ -384,7 +384,8 @@
     hwc2_layer_t getId() const { return mId; }
 
     [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y);
-    [[clang::warn_unused_result]] Error setBuffer(buffer_handle_t buffer,
+    [[clang::warn_unused_result]] Error setBuffer(uint32_t slot,
+            buffer_handle_t buffer,
             const android::sp<android::Fence>& acquireFence);
     [[clang::warn_unused_result]] Error setSurfaceDamage(
             const android::Region& damage);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7914770..e86a071 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -444,7 +444,7 @@
     }
 }
 
-status_t HWComposer::setClientTarget(int32_t displayId,
+status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot,
         const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
         android_dataspace_t dataspace) {
     if (!isValidDisplay(displayId)) {
@@ -457,7 +457,8 @@
     if ((target != nullptr) && target->getNativeBuffer()) {
         handle = target->getNativeBuffer()->handle;
     }
-    auto error = hwcDisplay->setClientTarget(handle, acquireFence, dataspace);
+    auto error = hwcDisplay->setClientTarget(slot, handle,
+            acquireFence, dataspace);
     if (error != HWC2::Error::None) {
         ALOGE("Failed to set client target for display %d: %s (%d)", displayId,
                 to_string(error).c_str(), static_cast<int32_t>(error));
@@ -894,5 +895,36 @@
     *this = DisplayData();
 }
 
+void HWComposerBufferCache::clear()
+{
+    mBuffers.clear();
+}
+
+void HWComposerBufferCache::getHwcBuffer(int slot,
+        const sp<GraphicBuffer>& buffer,
+        uint32_t* outSlot, sp<GraphicBuffer>* outBuffer)
+{
+    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
+        // default to slot 0
+        slot = 0;
+    }
+
+    if (static_cast<size_t>(slot) >= mBuffers.size()) {
+        mBuffers.resize(slot + 1);
+    }
+
+    *outSlot = slot;
+
+    if (mBuffers[slot] == buffer) {
+        // already cached in HWC, skip sending the buffer
+        *outBuffer = nullptr;
+    } else {
+        *outBuffer = buffer;
+
+        // update cache
+        mBuffers[slot] = buffer;
+    }
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 2713505..37f12e4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -26,6 +26,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <gui/BufferQueue.h>
+
 #include <ui/Fence.h>
 
 #include <utils/BitSet.h>
@@ -94,7 +96,8 @@
     // Asks the HAL what it can do
     status_t prepare(DisplayDevice& displayDevice);
 
-    status_t setClientTarget(int32_t displayId, const sp<Fence>& acquireFence,
+    status_t setClientTarget(int32_t displayId, uint32_t slot,
+            const sp<Fence>& acquireFence,
             const sp<GraphicBuffer>& target, android_dataspace_t dataspace);
 
     // Present layers to the display and read releaseFences.
@@ -224,6 +227,19 @@
     mutable Mutex mVsyncLock;
 };
 
+class HWComposerBufferCache {
+public:
+    void clear();
+
+    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer,
+            uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
+
+private:
+    // a vector as we expect "slot" to be in the range of [0, 63] (that is,
+    // less than BufferQueue::NUM_BUFFER_SLOTS).
+    std::vector<sp<GraphicBuffer>> mBuffers;
+};
+
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 6a98f03..5f3c388 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -221,9 +221,14 @@
     status_t result = NO_ERROR;
     if (fbBuffer != NULL) {
 #ifdef USE_HWC2
+        uint32_t hwcSlot = 0;
+        sp<GraphicBuffer> hwcBuffer;
+        mHwcBufferCache->getHwcBuffer(mFbProducerSlot, fbBuffer,
+                &hwcSlot, &hwcBuffer);
+
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(mDisplayId, mFbFence, fbBuffer,
-                HAL_DATASPACE_UNKNOWN);
+        result = mHwc.setClientTarget(mDisplayId, hwcSlot, mFbFence,
+                hwcBuffer, HAL_DATASPACE_UNKNOWN);
 #else
         result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
 #endif
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index d37dc0a..2636667 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -17,16 +17,19 @@
 #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 
+#include "DisplaySurface.h"
+
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 
-#include "DisplaySurface.h"
+#include <memory>
 
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
 
 class HWComposer;
+class HWComposerBufferCache;
 class IProducerListener;
 
 /* This DisplaySurface implementation supports virtual displays, where GLES
@@ -250,6 +253,11 @@
     static const char* dbgSourceStr(Source s);
 
     bool mMustRecompose;
+
+#ifdef USE_HWC2
+    std::unique_ptr<HWComposerBufferCache> mHwcBufferCache =
+        std::make_unique<HWComposerBufferCache>();
+#endif
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e57c19a..3a9bca6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -270,6 +270,16 @@
     }
 }
 
+void Layer::onBuffersReleased() {
+#ifdef USE_HWC2
+    Mutex::Autolock lock(mHwcBufferCacheMutex);
+
+    for (auto info : mHwcBufferCaches) {
+        info.second.clear();
+    }
+#endif
+}
+
 void Layer::onSidebandStreamChanged() {
     if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) {
         // mSidebandStreamChanged was false
@@ -836,8 +846,22 @@
               static_cast<int32_t>(error));
     }
 
+    uint32_t hwcSlot = 0;
+    buffer_handle_t hwcHandle = nullptr;
+    {
+        Mutex::Autolock lock(mHwcBufferCacheMutex);
+
+        auto& hwcBufferCache = mHwcBufferCaches[hwcId];
+        sp<GraphicBuffer> hwcBuffer;
+        hwcBufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer,
+                &hwcSlot, &hwcBuffer);
+        if (hwcBuffer != nullptr) {
+            hwcHandle = hwcBuffer->handle;
+        }
+    }
+
     auto acquireFence = mSurfaceFlingerConsumer->getCurrentFence();
-    error = hwcLayer->setBuffer(mActiveBuffer->handle, acquireFence);
+    error = hwcLayer->setBuffer(hwcSlot, hwcHandle, acquireFence);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
                 mActiveBuffer->handle, to_string(error).c_str(),
@@ -2087,7 +2111,8 @@
     }
 
     // update the active buffer
-    mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
+    mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer(
+            &mActiveBufferSlot);
     if (mActiveBuffer == NULL) {
         // this can only happen if the very first buffer was rejected.
         return outDirtyRegion;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7335be7..76c710a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -376,13 +376,20 @@
 #ifdef USE_HWC2
     // -----------------------------------------------------------------------
 
+    void eraseHwcLayer(int32_t hwcId) {
+        mHwcLayers.erase(hwcId);
+
+        Mutex::Autolock lock(mHwcBufferCacheMutex);
+        mHwcBufferCaches.erase(hwcId);
+    }
+
     bool hasHwcLayer(int32_t hwcId) {
         if (mHwcLayers.count(hwcId) == 0) {
             return false;
         }
         if (mHwcLayers[hwcId].layer->isAbandoned()) {
             ALOGI("Erasing abandoned layer %s on %d", mName.string(), hwcId);
-            mHwcLayers.erase(hwcId);
+            eraseHwcLayer(hwcId);
             return false;
         }
         return true;
@@ -398,8 +405,11 @@
     void setHwcLayer(int32_t hwcId, std::shared_ptr<HWC2::Layer>&& layer) {
         if (layer) {
             mHwcLayers[hwcId].layer = layer;
+
+            Mutex::Autolock lock(mHwcBufferCacheMutex);
+            mHwcBufferCaches[hwcId] = HWComposerBufferCache();
         } else {
-            mHwcLayers.erase(hwcId);
+            eraseHwcLayer(hwcId);
         }
     }
 
@@ -489,6 +499,7 @@
     // Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
     virtual void onFrameAvailable(const BufferItem& item) override;
     virtual void onFrameReplaced(const BufferItem& item) override;
+    virtual void onBuffersReleased() override;
     virtual void onSidebandStreamChanged() override;
 
     void commitTransaction(const State& stateToCommit);
@@ -642,6 +653,7 @@
     FenceTimeline mReleaseTimeline;
 
     // main thread
+    int mActiveBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<GraphicBuffer> mActiveBuffer;
     sp<NativeHandle> mSidebandStream;
     Rect mCurrentCrop;
@@ -681,6 +693,12 @@
         gfx::FloatRect sourceCrop;
     };
     std::unordered_map<int32_t, HWCInfo> mHwcLayers;
+
+    // We need one HWComposerBufferCache for each HWC display.  We cannot have
+    // HWComposerBufferCache in HWCInfo because HWCInfo can only be accessed
+    // from the main thread.
+    Mutex mHwcBufferCacheMutex;
+    std::unordered_map<int32_t, HWComposerBufferCache> mHwcBufferCaches;
 #else
     bool mIsGlesComposition;
 #endif
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 029937a..942af13 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -235,6 +235,19 @@
     mContentsChangedListener = listener;
 }
 
+void SurfaceFlingerConsumer::onBuffersReleased() {
+    sp<ContentsChangedListener> listener;
+    {   // scope for the lock
+        Mutex::Autolock lock(mMutex);
+        ALOG_ASSERT(mFrameAvailableListener.unsafe_get() == mContentsChangedListener.unsafe_get());
+        listener = mContentsChangedListener.promote();
+    }
+
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+}
+
 void SurfaceFlingerConsumer::onSidebandStreamChanged() {
     sp<ContentsChangedListener> listener;
     {   // scope for the lock
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index d3f0070..7713ed2 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -33,6 +33,7 @@
     static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
 
     struct ContentsChangedListener: public FrameAvailableListener {
+        virtual void onBuffersReleased() = 0;
         virtual void onSidebandStreamChanged() = 0;
     };
 
@@ -89,6 +90,7 @@
             FrameEventHistoryDelta* outDelta) override;
 
 private:
+    virtual void onBuffersReleased();
     virtual void onSidebandStreamChanged();
 
     wp<ContentsChangedListener> mContentsChangedListener;