|  | /* | 
|  | * Copyright 2019 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include <compositionengine/impl/HwcBufferCache.h> | 
|  | #include <gtest/gtest.h> | 
|  | #include <gui/BufferQueue.h> | 
|  | #include <ui/GraphicBuffer.h> | 
|  |  | 
|  | namespace android::compositionengine { | 
|  | namespace { | 
|  |  | 
|  | using impl::HwcBufferCache; | 
|  | using impl::HwcSlotAndBuffer; | 
|  |  | 
|  | class HwcBufferCacheTest : public testing::Test { | 
|  | public: | 
|  | ~HwcBufferCacheTest() override = default; | 
|  |  | 
|  | sp<GraphicBuffer> mBuffer1 = | 
|  | sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); | 
|  | sp<GraphicBuffer> mBuffer2 = | 
|  | sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); | 
|  | }; | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_returnsUniqueSlotNumberForEachBuffer) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1); | 
|  | EXPECT_NE(slotAndBufferFor1.slot, UINT32_MAX); | 
|  | EXPECT_EQ(slotAndBufferFor1.buffer, mBuffer1); | 
|  |  | 
|  | HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2); | 
|  | EXPECT_NE(slotAndBufferFor2.slot, slotAndBufferFor1.slot); | 
|  | EXPECT_NE(slotAndBufferFor2.slot, UINT32_MAX); | 
|  | EXPECT_EQ(slotAndBufferFor2.buffer, mBuffer2); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenCached_returnsSameSlotNumberAndNullBuffer) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1); | 
|  | EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX); | 
|  | EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1); | 
|  |  | 
|  | HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1); | 
|  | EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot); | 
|  | EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenSlotsFull_evictsOldestCachedBuffer) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | sp<GraphicBuffer> graphicBuffers[100]; | 
|  | HwcSlotAndBuffer slotsAndBuffers[100]; | 
|  | int finalCachedBufferIndex = 0; | 
|  | for (int i = 0; i < 100; ++i) { | 
|  | graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); | 
|  | slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]); | 
|  | // we fill up the cache when the slot number for the first buffer is reused | 
|  | if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) { | 
|  | finalCachedBufferIndex = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  | ASSERT_GT(finalCachedBufferIndex, 1); | 
|  | // the final cached buffer has the same slot value as the oldest buffer | 
|  | EXPECT_EQ(slotsAndBuffers[finalCachedBufferIndex].slot, slotsAndBuffers[0].slot); | 
|  | // the oldest buffer is no longer in the cache because it was evicted | 
|  | EXPECT_EQ(cache.uncache(graphicBuffers[0]->getId()), UINT32_MAX); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, uncache_whenCached_returnsSlotNumber) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1); | 
|  | ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX); | 
|  |  | 
|  | HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2); | 
|  | ASSERT_NE(slotAndBufferFor2.slot, UINT32_MAX); | 
|  |  | 
|  | // the 1st buffer should be found in the cache with a slot number | 
|  | EXPECT_EQ(cache.uncache(mBuffer1->getId()), slotAndBufferFor1.slot); | 
|  | // since the 1st buffer has been previously uncached, we should no longer receive a slot number | 
|  | EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX); | 
|  | // the 2nd buffer should be still found in the cache with a slot number | 
|  | EXPECT_EQ(cache.uncache(mBuffer2->getId()), slotAndBufferFor2.slot); | 
|  | // since the 2nd buffer has been previously uncached, we should no longer receive a slot number | 
|  | EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, uncache_whenUncached_returnsInvalidSlotNumber) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1); | 
|  | ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX); | 
|  |  | 
|  | EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenCached_returnsSameSlotAndNullBuffer) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | HwcSlotAndBuffer originalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1); | 
|  | EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX); | 
|  | EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1); | 
|  |  | 
|  | HwcSlotAndBuffer finalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1); | 
|  | EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot); | 
|  | EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenSlotsFull_returnsIndependentSlot) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | sp<GraphicBuffer> graphicBuffers[100]; | 
|  | HwcSlotAndBuffer slotsAndBuffers[100]; | 
|  | int finalCachedBufferIndex = -1; | 
|  | for (int i = 0; i < 100; ++i) { | 
|  | graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); | 
|  | slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]); | 
|  | // we fill up the cache when the slot number for the first buffer is reused | 
|  | if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) { | 
|  | finalCachedBufferIndex = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  | // expect to have cached at least a few buffers before evicting | 
|  | ASSERT_GT(finalCachedBufferIndex, 1); | 
|  |  | 
|  | sp<GraphicBuffer> overrideBuffer = | 
|  | sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u); | 
|  | HwcSlotAndBuffer overrideSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(overrideBuffer); | 
|  | // expect us to have a slot number | 
|  | EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX); | 
|  | // expect this to be the first time we cached the buffer | 
|  | EXPECT_NE(overrideSlotAndBuffer.buffer, nullptr); | 
|  |  | 
|  | // expect the slot number to not equal any other slot number, even after the slots have been | 
|  | // exhausted, indicating that the override buffer slot is independent from the slots for | 
|  | // non-override buffers | 
|  | for (int i = 0; i < finalCachedBufferIndex; ++i) { | 
|  | EXPECT_NE(overrideSlotAndBuffer.slot, slotsAndBuffers[i].slot); | 
|  | } | 
|  | // the override buffer is independently uncached from the oldest cached buffer | 
|  | // expect to find the override buffer still in the override buffer slot | 
|  | EXPECT_EQ(cache.uncache(overrideBuffer->getId()), overrideSlotAndBuffer.slot); | 
|  | // expect that the first buffer was not evicted from the cache when the override buffer was | 
|  | // cached | 
|  | EXPECT_EQ(cache.uncache(graphicBuffers[1]->getId()), slotsAndBuffers[1].slot); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, uncache_whenOverrideCached_returnsSlotNumber) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1); | 
|  | ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX); | 
|  |  | 
|  | EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot); | 
|  | EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX); | 
|  | } | 
|  |  | 
|  | TEST_F(HwcBufferCacheTest, uncache_whenOverrideUncached_returnsInvalidSlotNumber) { | 
|  | HwcBufferCache cache; | 
|  | sp<GraphicBuffer> outBuffer; | 
|  |  | 
|  | HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1); | 
|  | ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX); | 
|  |  | 
|  | EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX); | 
|  | } | 
|  |  | 
|  | } // namespace | 
|  | } // namespace android::compositionengine |