| /* |
| * Copyright 2022 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. |
| */ |
| |
| #undef LOG_TAG |
| #define LOG_TAG "HwcSlotGenerator" |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| #include <gui/BufferQueue.h> |
| |
| #include "HwcSlotGenerator.h" |
| |
| namespace android { |
| |
| HwcSlotGenerator::HwcSlotGenerator() { |
| for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { |
| mFreeHwcCacheSlots.push(i); |
| } |
| } |
| |
| void HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) { |
| std::lock_guard lock(mMutex); |
| if (!clientCacheId.isValid()) { |
| ALOGE("invalid process, failed to erase buffer"); |
| return; |
| } |
| eraseBufferLocked(clientCacheId); |
| } |
| |
| int HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) { |
| std::lock_guard<std::mutex> lock(mMutex); |
| auto itr = mCachedBuffers.find(clientCacheId); |
| if (itr == mCachedBuffers.end()) { |
| return addCachedBuffer(clientCacheId); |
| } |
| auto& [hwcCacheSlot, counter] = itr->second; |
| counter = mCounter++; |
| return hwcCacheSlot; |
| } |
| |
| int HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex) { |
| if (!clientCacheId.isValid()) { |
| ALOGE("invalid process, returning invalid slot"); |
| return BufferQueue::INVALID_BUFFER_SLOT; |
| } |
| |
| ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this)); |
| |
| int hwcCacheSlot = getFreeHwcCacheSlot(); |
| mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++}; |
| return hwcCacheSlot; |
| } |
| |
| int HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) { |
| if (mFreeHwcCacheSlots.empty()) { |
| evictLeastRecentlyUsed(); |
| } |
| |
| int hwcCacheSlot = mFreeHwcCacheSlots.top(); |
| mFreeHwcCacheSlots.pop(); |
| return hwcCacheSlot; |
| } |
| |
| void HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) { |
| uint64_t minCounter = UINT_MAX; |
| client_cache_t minClientCacheId = {}; |
| for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) { |
| const auto& [hwcCacheSlot, counter] = slotCounter; |
| if (counter < minCounter) { |
| minCounter = counter; |
| minClientCacheId = clientCacheId; |
| } |
| } |
| eraseBufferLocked(minClientCacheId); |
| |
| ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId, this); |
| } |
| |
| void HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex) { |
| auto itr = mCachedBuffers.find(clientCacheId); |
| if (itr == mCachedBuffers.end()) { |
| return; |
| } |
| auto& [hwcCacheSlot, counter] = itr->second; |
| |
| // TODO send to hwc cache and resources |
| |
| mFreeHwcCacheSlots.push(hwcCacheSlot); |
| mCachedBuffers.erase(clientCacheId); |
| } |
| |
| } // namespace android |