Loading services/surfaceflinger/BufferQueueLayer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -366,7 +366,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); Loading services/surfaceflinger/BufferStateLayer.cpp +6 −3 Original line number Diff line number Diff line Loading @@ -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)); Loading services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h +16 −6 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <cstdint> #include <vector> #include <gui/BufferQueue.h> #include <utils/StrongPointer.h> namespace android { Loading @@ -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 Loading services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp +27 −13 Original line number Diff line number Diff line Loading @@ -21,31 +21,45 @@ 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)); } 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; } } 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; // use the least-recently used slot return getLeastRecentlyUsedSlot(); } if (static_cast<size_t>(slot) >= mBuffers.size()) { mBuffers.resize(slot + 1); int HwcBufferCache::getLeastRecentlyUsedSlot() { auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers)); return std::distance(std::begin(mBuffers), iter); } *outSlot = slot; 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 services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp +60 −42 Original line number Diff line number Diff line Loading @@ -22,60 +22,78 @@ 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) { 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, testSlot) { 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); mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer); EXPECT_EQ(0, 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); 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(inSlot, mBuffer2, &outSlot, &outBuffer); EXPECT_EQ(expectedSlot, outSlot); 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(inSlot, mBuffer2, &outSlot, &outBuffer); EXPECT_EQ(expectedSlot, outSlot); 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(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer); EXPECT_EQ(expectedSlot, outSlot); mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer); EXPECT_EQ(2, outSlot); EXPECT_EQ(nullptr, outBuffer.get()); } impl::HwcBufferCache 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, testGetLeastRecentlyUsedSlot) { int slot; uint32_t outSlot; sp<GraphicBuffer> outBuffer; TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) { testSlot(0, 0); // 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); } TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) { testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1); } slot = mCache.getLeastRecentlyUsedSlot(); EXPECT_EQ(0, slot); TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) { testSlot(-123, 0); } mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer); EXPECT_EQ(0, outSlot); EXPECT_EQ(mBuffer1, outBuffer); TEST_F(HwcBufferCacheTest, cacheMapsInvalidBufferSlotToZero) { testSlot(BufferQueue::INVALID_BUFFER_SLOT, 0); slot = mCache.getLeastRecentlyUsedSlot(); EXPECT_EQ(1, slot); } } // namespace Loading Loading
services/surfaceflinger/BufferQueueLayer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -366,7 +366,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); Loading
services/surfaceflinger/BufferStateLayer.cpp +6 −3 Original line number Diff line number Diff line Loading @@ -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)); Loading
services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h +16 −6 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <cstdint> #include <vector> #include <gui/BufferQueue.h> #include <utils/StrongPointer.h> namespace android { Loading @@ -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 Loading
services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp +27 −13 Original line number Diff line number Diff line Loading @@ -21,31 +21,45 @@ 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)); } 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; } } 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; // use the least-recently used slot return getLeastRecentlyUsedSlot(); } if (static_cast<size_t>(slot) >= mBuffers.size()) { mBuffers.resize(slot + 1); int HwcBufferCache::getLeastRecentlyUsedSlot() { auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers)); return std::distance(std::begin(mBuffers), iter); } *outSlot = slot; 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
services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp +60 −42 Original line number Diff line number Diff line Loading @@ -22,60 +22,78 @@ 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) { 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, testSlot) { 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); mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer); EXPECT_EQ(0, 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); 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(inSlot, mBuffer2, &outSlot, &outBuffer); EXPECT_EQ(expectedSlot, outSlot); 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(inSlot, mBuffer2, &outSlot, &outBuffer); EXPECT_EQ(expectedSlot, outSlot); 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(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer); EXPECT_EQ(expectedSlot, outSlot); mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer); EXPECT_EQ(2, outSlot); EXPECT_EQ(nullptr, outBuffer.get()); } impl::HwcBufferCache 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, testGetLeastRecentlyUsedSlot) { int slot; uint32_t outSlot; sp<GraphicBuffer> outBuffer; TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) { testSlot(0, 0); // 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); } TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) { testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1); } slot = mCache.getLeastRecentlyUsedSlot(); EXPECT_EQ(0, slot); TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) { testSlot(-123, 0); } mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer); EXPECT_EQ(0, outSlot); EXPECT_EQ(mBuffer1, outBuffer); TEST_F(HwcBufferCacheTest, cacheMapsInvalidBufferSlotToZero) { testSlot(BufferQueue::INVALID_BUFFER_SLOT, 0); slot = mCache.getLeastRecentlyUsedSlot(); EXPECT_EQ(1, slot); } } // namespace Loading