OSDN Git Service

Caching between SF and HWC for BufferStateLayers
authorValerie Hau <vhau@google.com>
Wed, 20 Feb 2019 18:36:29 +0000 (10:36 -0800)
committerValerie Hau <vhau@google.com>
Wed, 27 Feb 2019 15:45:04 +0000 (07:45 -0800)
Bug: 120434937
Test: build, boot,libcompositionengine_test
Change-Id: I4a99faeb7aa88683aac19c7000db87bd75f9ebe9

services/surfaceflinger/BufferQueueLayer.cpp
services/surfaceflinger/BufferStateLayer.cpp
services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp

index 96c4992..76764ef 100644 (file)
@@ -367,7 +367,7 @@ void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display)
     uint32_t hwcSlot = 0;
     sp<GraphicBuffer> hwcBuffer;
     (*outputLayer->editState().hwc)
-            .hwcBufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer);
+            .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer);
 
     auto acquireFence = mConsumer->getCurrentFence();
     auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
index f6b69eb..a6c090c 100644 (file)
@@ -565,14 +565,17 @@ status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
 void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
     const auto outputLayer = findOutputLayerForDisplay(display);
     LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
+    auto& hwcInfo = *outputLayer->editState().hwc;
+    auto& hwcLayer = hwcInfo.hwcLayer;
 
     const State& s(getDrawingState());
 
-    // TODO(marissaw): support more than one slot
+    // obtain slot
     uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> buffer;
+    hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer);
 
-    auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence);
+    auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
               s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
index b45de5a..3b4f6e4 100644 (file)
@@ -19,6 +19,7 @@
 #include <cstdint>
 #include <vector>
 
+#include <gui/BufferQueue.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
@@ -39,19 +40,28 @@ namespace compositionengine::impl {
 class HwcBufferCache {
 public:
     HwcBufferCache();
-
-    // Given a buffer queue slot and buffer, return the HWC cache slot and
+    // Given a buffer, return the HWC cache slot and
     // buffer to be sent to HWC.
     //
     // outBuffer is set to buffer when buffer is not in the HWC cache;
     // otherwise, outBuffer is set to nullptr.
-    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+    void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                       sp<GraphicBuffer>* outBuffer);
 
+protected:
+    int getSlot(const sp<GraphicBuffer>& buffer);
+    int getLeastRecentlyUsedSlot();
+    uint64_t getCounter();
+
 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;
+    // an array where the index corresponds to a slot and the value corresponds to a (counter,
+    // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
+    // or used and allows us to keep track of the least-recently used buffer.
+    std::pair<uint64_t, wp<GraphicBuffer>> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+
+    // The cache increments this counter value when a slot is updated or used.
+    // Used to track the least recently-used buffer
+    uint64_t mCounter = 1;
 };
 
 } // namespace compositionengine::impl
index 6f340b9..8177c7f 100644 (file)
 namespace android::compositionengine::impl {
 
 HwcBufferCache::HwcBufferCache() {
-    mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS);
+    std::fill(std::begin(mBuffers), std::end(mBuffers),
+              std::pair<uint64_t, wp<GraphicBuffer>>(0, nullptr));
 }
-
-void HwcBufferCache::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;
+int HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer) {
+    // search for cached buffer first
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        if (mBuffers[i].second == buffer) {
+            return i;
+        }
     }
 
-    if (static_cast<size_t>(slot) >= mBuffers.size()) {
-        mBuffers.resize(slot + 1);
-    }
+    // use the least-recently used slot
+    return getLeastRecentlyUsedSlot();
+}
 
-    *outSlot = slot;
+int HwcBufferCache::getLeastRecentlyUsedSlot() {
+    auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers));
+    return std::distance(std::begin(mBuffers), iter);
+}
+
+void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+                                  sp<GraphicBuffer>* outBuffer) {
+    *outSlot = getSlot(buffer);
 
-    if (mBuffers[slot] == buffer) {
+    auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
+    if (currentBuffer == buffer) {
         // already cached in HWC, skip sending the buffer
         *outBuffer = nullptr;
+        currentCounter = getCounter();
     } else {
         *outBuffer = buffer;
 
         // update cache
-        mBuffers[slot] = buffer;
+        currentBuffer = buffer;
+        currentCounter = getCounter();
     }
 }
 
+uint64_t HwcBufferCache::getCounter() {
+    return mCounter++;
+}
 } // namespace android::compositionengine::impl
index f2a1aad..d4bbf6d 100644 (file)
 namespace android::compositionengine {
 namespace {
 
+class TestableHwcBufferCache : public impl::HwcBufferCache {
+public:
+    void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+                      sp<GraphicBuffer>* outBuffer) {
+        HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer);
+    }
+    int getSlot(const sp<GraphicBuffer>& buffer) { return HwcBufferCache::getSlot(buffer); }
+    int getLeastRecentlyUsedSlot() { return HwcBufferCache::getLeastRecentlyUsedSlot(); }
+};
+
 class HwcBufferCacheTest : public testing::Test {
 public:
     ~HwcBufferCacheTest() override = default;
 
-    void testSlot(const int inSlot, const uint32_t expectedSlot) {
-        uint32_t outSlot;
-        sp<GraphicBuffer> outBuffer;
-
-        // The first time, the output  is the same as the input
-        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(mBuffer1, outBuffer);
-
-        // The second time with the same buffer, the outBuffer is nullptr.
-        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-
-        // With a new buffer, the outBuffer is the input.
-        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(mBuffer2, outBuffer);
-
-        // Again, the second request with the same buffer sets outBuffer to nullptr.
-        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-
-        // Setting a slot to use nullptr lookslike works, but note that
-        // the output values make it look like no new buffer is being set....
-        mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-    }
-
-    impl::HwcBufferCache mCache;
+    TestableHwcBufferCache mCache;
     sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
     sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 };
 
-TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
-    testSlot(0, 0);
-}
+TEST_F(HwcBufferCacheTest, testSlot) {
+    uint32_t outSlot;
+    sp<GraphicBuffer> outBuffer;
 
-TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
-    testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
-}
+    // The first time, the output  is the same as the input
+    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+    EXPECT_EQ(0, outSlot);
+    EXPECT_EQ(mBuffer1, outBuffer);
 
-TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
-    testSlot(-123, 0);
+    // The second time with the same buffer, the outBuffer is nullptr.
+    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+    EXPECT_EQ(0, outSlot);
+    EXPECT_EQ(nullptr, outBuffer.get());
+
+    // With a new buffer, the outBuffer is the input.
+    mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+    EXPECT_EQ(1, outSlot);
+    EXPECT_EQ(mBuffer2, outBuffer);
+
+    // Again, the second request with the same buffer sets outBuffer to nullptr.
+    mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+    EXPECT_EQ(1, outSlot);
+    EXPECT_EQ(nullptr, outBuffer.get());
+
+    // Setting a slot to use nullptr lookslike works, but note that
+    // the output values make it look like no new buffer is being set....
+    mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer);
+    EXPECT_EQ(2, outSlot);
+    EXPECT_EQ(nullptr, outBuffer.get());
 }
 
-TEST_F(HwcBufferCacheTest, cacheMapsInvalidBufferSlotToZero) {
-    testSlot(BufferQueue::INVALID_BUFFER_SLOT, 0);
+TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) {
+    int slot;
+    uint32_t outSlot;
+    sp<GraphicBuffer> outBuffer;
+
+    // fill up cache
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        mCache.getHwcBuffer(buf, &outSlot, &outBuffer);
+        EXPECT_EQ(buf, outBuffer);
+        EXPECT_EQ(i, outSlot);
+    }
+
+    slot = mCache.getLeastRecentlyUsedSlot();
+    EXPECT_EQ(0, slot);
+
+    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+    EXPECT_EQ(0, outSlot);
+    EXPECT_EQ(mBuffer1, outBuffer);
+
+    slot = mCache.getLeastRecentlyUsedSlot();
+    EXPECT_EQ(1, slot);
 }
 
 } // namespace
index 27812f7..775fb80 100644 (file)
@@ -111,8 +111,7 @@ status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
     BufferItem item;
     status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
-        mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
-                &outSlot, &outBuffer);
+        mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
         return NO_ERROR;
     } else if (err != NO_ERROR) {
         ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -138,8 +137,7 @@ status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
     mCurrentFence = item.mFence;
 
     outFence = item.mFence;
-    mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
-            &outSlot, &outBuffer);
+    mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
     outDataspace = static_cast<Dataspace>(item.mDataSpace);
     status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
     if (result != NO_ERROR) {
index 1c2853a..613dc77 100644 (file)
@@ -224,8 +224,7 @@ status_t VirtualDisplaySurface::advanceFrame() {
     if (fbBuffer != nullptr) {
         uint32_t hwcSlot = 0;
         sp<GraphicBuffer> hwcBuffer;
-        mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer,
-                &hwcSlot, &hwcBuffer);
+        mHwcBufferCache.getHwcBuffer(fbBuffer, &hwcSlot, &hwcBuffer);
 
         // TODO: Correctly propagate the dataspace from GL composition
         result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,