diff options
author | 2019-03-29 14:03:53 -0700 | |
---|---|---|
committer | 2019-04-16 15:39:28 -0700 | |
commit | 947d34ecad84bdaf496748eeb9f6e35b33deb339 (patch) | |
tree | 29f668d1d193b97ab93d74b0d4cdc7b0bd239c3f | |
parent | 1688f5246f156e815bd7b07ba7d5c39cc821e8d6 (diff) |
Change slot generation for BufferState
BufferState layers now do slot generation with buffer death considered
appropriately. When a buffer dies, the slot will be pushed onto a stack
of available slots to be reused at the next opportunity. This should
mimic BufferQueue slot behavior and prevent Composer Resources from
growing too large.
Test: build, boot, manual
Bug: 129351223
Change-Id: Icef9592593cacb0b5c6b12f6679fc2c4dabdcd19
18 files changed, 399 insertions, 249 deletions
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index a3165ddb9e..a8b1a4c7af 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -69,7 +69,7 @@ public: const sp<IBinder>& applyToken, const InputWindowCommands& commands, int64_t desiredPresentTime, - const cached_buffer_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, const std::vector<ListenerCallbacks>& listenerCallbacks) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -88,8 +88,8 @@ public: data.writeStrongBinder(applyToken); commands.write(data); data.writeInt64(desiredPresentTime); - data.writeStrongBinder(uncacheBuffer.token); - data.writeUint64(uncacheBuffer.cacheId); + data.writeWeakBinder(uncacheBuffer.token); + data.writeUint64(uncacheBuffer.id); if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) { for (const auto& [listener, callbackIds] : listenerCallbacks) { @@ -991,9 +991,9 @@ status_t BnSurfaceComposer::onTransact( int64_t desiredPresentTime = data.readInt64(); - cached_buffer_t uncachedBuffer; - uncachedBuffer.token = data.readStrongBinder(); - uncachedBuffer.cacheId = data.readUint64(); + client_cache_t uncachedBuffer; + uncachedBuffer.token = data.readWeakBinder(); + uncachedBuffer.id = data.readUint64(); std::vector<ListenerCallbacks> listenerCallbacks; int32_t listenersSize = data.readInt32(); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 3077b21da7..075bb52e3c 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -87,8 +87,8 @@ status_t layer_state_t::write(Parcel& output) const colorTransform.asArray(), 16 * sizeof(float)); output.writeFloat(cornerRadius); output.writeBool(hasListenerCallbacks); - output.writeStrongBinder(cachedBuffer.token); - output.writeUint64(cachedBuffer.cacheId); + output.writeWeakBinder(cachedBuffer.token); + output.writeUint64(cachedBuffer.id); output.writeParcelable(metadata); output.writeFloat(bgColorAlpha); @@ -157,8 +157,8 @@ status_t layer_state_t::read(const Parcel& input) colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); cornerRadius = input.readFloat(); hasListenerCallbacks = input.readBool(); - cachedBuffer.token = input.readStrongBinder(); - cachedBuffer.cacheId = input.readUint64(); + cachedBuffer.token = input.readWeakBinder(); + cachedBuffer.id = input.readUint64(); input.readParcelable(&metadata); bgColorAlpha = input.readFloat(); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 611da89ef6..a19be12d61 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -387,9 +387,9 @@ void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - cached_buffer_t uncacheBuffer; + client_cache_t uncacheBuffer; uncacheBuffer.token = BufferCache::getInstance().getToken(); - uncacheBuffer.cacheId = cacheId; + uncacheBuffer.id = cacheId; sf->setTransactionState({}, {}, 0, nullptr, {}, -1, uncacheBuffer, {}); } @@ -422,7 +422,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } s->what |= layer_state_t::eCachedBufferChanged; s->cachedBuffer.token = BufferCache::getInstance().getToken(); - s->cachedBuffer.cacheId = cacheId; + s->cachedBuffer.id = cacheId; // If we have more buffers than the size of the cache, we should stop caching so we don't // evict other buffers in this transaction diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 415b2d58ec..e8c7a39ad7 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -44,7 +44,7 @@ namespace android { // ---------------------------------------------------------------------------- -struct cached_buffer_t; +struct client_cache_t; struct ComposerState; struct DisplayState; struct DisplayInfo; @@ -137,7 +137,7 @@ public: const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, - const cached_buffer_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, const std::vector<ListenerCallbacks>& listenerCallbacks) = 0; /* signal that we're done booting. diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 2256497751..f438eb3d01 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -40,9 +40,13 @@ namespace android { class Parcel; class ISurfaceComposerClient; -struct cached_buffer_t { - sp<IBinder> token = nullptr; - uint64_t cacheId; +struct client_cache_t { + wp<IBinder> token = nullptr; + uint64_t id; + + bool operator==(const client_cache_t& other) const { return id == other.id; } + + bool isValid() const { return token != nullptr; } }; /* @@ -187,7 +191,7 @@ struct layer_state_t { InputWindowInfo inputInfo; #endif - cached_buffer_t cachedBuffer; + client_cache_t cachedBuffer; LayerMetadata metadata; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index d5bdd74a86..014b1fac7d 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -561,8 +561,7 @@ public: const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, const sp<IBinder>& /*applyToken*/, const InputWindowCommands& /*inputWindowCommands*/, - int64_t /*desiredPresentTime*/, - const cached_buffer_t& /*cachedBuffer*/, + int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/, const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override { } diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 9080d29194..4cd0a13861 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -116,7 +116,7 @@ filegroup { "BufferLayerConsumer.cpp", "BufferQueueLayer.cpp", "BufferStateLayer.cpp", - "BufferStateLayerCache.cpp", + "ClientCache.cpp", "Client.cpp", "ColorLayer.cpp", "ContainerLayer.cpp", diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 2cbb917478..6ef4518a47 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -26,6 +26,7 @@ #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> +#include <gui/BufferQueue.h> #include <private/gui/SyncFeatures.h> #include <renderengine/Image.h> @@ -44,7 +45,8 @@ const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{ }; // clang-format on -BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) { +BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) + : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) { mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; mCurrentState.dataspace = ui::Dataspace::V0_SRGB; } @@ -210,12 +212,13 @@ bool BufferStateLayer::setFrame(const Rect& frame) { } bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime, - nsecs_t desiredPresentTime) { + nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) { if (mCurrentState.buffer) { mReleasePreviousBuffer = true; } mCurrentState.buffer = buffer; + mCurrentState.clientCacheId = clientCacheId; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -589,8 +592,8 @@ void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) uint32_t hwcSlot; sp<GraphicBuffer> buffer; - hwcInfo.hwcBufferCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, s.buffer, &hwcSlot, - &buffer); + hwcInfo.hwcBufferCache.getHwcBuffer(mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId), + s.buffer, &hwcSlot, &buffer); auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence); if (error != HWC2::Error::None) { @@ -609,4 +612,76 @@ void BufferStateLayer::onFirstRef() { } } +void BufferStateLayer::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); +} + +uint32_t BufferStateLayer::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; +} + +uint32_t BufferStateLayer::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)); + + uint32_t hwcCacheSlot = getFreeHwcCacheSlot(); + mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++}; + return hwcCacheSlot; +} + +uint32_t BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) { + if (mFreeHwcCacheSlots.empty()) { + evictLeastRecentlyUsed(); + } + + uint32_t hwcCacheSlot = mFreeHwcCacheSlots.top(); + mFreeHwcCacheSlots.pop(); + return hwcCacheSlot; +} + +void BufferStateLayer::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 BufferStateLayer::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 diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 864a15db1f..12f0141d5c 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -25,6 +25,8 @@ #include <system/window.h> #include <utils/String8.h> +#include <stack> + namespace android { class BufferStateLayer : public BufferLayer { @@ -63,8 +65,8 @@ public: bool setTransformToDisplayInverse(bool transformToDisplayInverse) override; bool setCrop(const Rect& crop) override; bool setFrame(const Rect& frame) override; - bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime, - nsecs_t desiredPresentTime) override; + bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime, nsecs_t desiredPresentTime, + const client_cache_t& clientCacheId) override; bool setAcquireFence(const sp<Fence>& fence) override; bool setDataspace(ui::Dataspace dataspace) override; bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; @@ -154,6 +156,45 @@ private: nsecs_t mDesiredPresentTime = -1; // TODO(marissaw): support sticky transform for LEGACY camera mode + + class HwcSlotGenerator : public ClientCache::ErasedRecipient { + public: + HwcSlotGenerator() { + for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + mFreeHwcCacheSlots.push(i); + } + } + + void bufferErased(const client_cache_t& clientCacheId); + + uint32_t getHwcCacheSlot(const client_cache_t& clientCacheId); + + private: + uint32_t addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex); + uint32_t getFreeHwcCacheSlot() REQUIRES(mMutex); + void evictLeastRecentlyUsed() REQUIRES(mMutex); + void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex); + + struct CachedBufferHash { + std::size_t operator()(const client_cache_t& clientCacheId) const { + return std::hash<uint64_t>{}(clientCacheId.id); + } + }; + + std::mutex mMutex; + + std::unordered_map<client_cache_t, + std::pair<uint32_t /*HwcCacheSlot*/, uint32_t /*counter*/>, + CachedBufferHash> + mCachedBuffers GUARDED_BY(mMutex); + std::stack<uint32_t /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex); + + // The cache increments this counter value when a slot is updated or used. + // Used to track the least recently-used buffer + uint64_t mCounter = 0; + }; + + sp<HwcSlotGenerator> mHwcSlotGenerator; }; } // namespace android diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp deleted file mode 100644 index 51ca45c53a..0000000000 --- a/services/surfaceflinger/BufferStateLayerCache.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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. - */ - -//#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "BufferStateLayerCache" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include <cinttypes> - -#include "BufferStateLayerCache.h" - -namespace android { - -ANDROID_SINGLETON_STATIC_INSTANCE(BufferStateLayerCache); - -BufferStateLayerCache::BufferStateLayerCache() : mDeathRecipient(new CacheDeathRecipient) {} - -void BufferStateLayerCache::add(const sp<IBinder>& processToken, uint64_t id, - const sp<GraphicBuffer>& buffer) { - if (!processToken) { - ALOGE("failed to cache buffer: invalid process token"); - return; - } - - if (!buffer) { - ALOGE("failed to cache buffer: invalid buffer"); - return; - } - - std::lock_guard lock(mMutex); - - // If this is a new process token, set a death recipient. If the client process dies, we will - // get a callback through binderDied. - if (mBuffers.find(processToken) == mBuffers.end()) { - status_t err = processToken->linkToDeath(mDeathRecipient); - if (err != NO_ERROR) { - ALOGE("failed to cache buffer: could not link to death"); - return; - } - } - - auto& processBuffers = mBuffers[processToken]; - - if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) { - ALOGE("failed to cache buffer: cache is full"); - return; - } - - processBuffers[id] = buffer; -} - -void BufferStateLayerCache::erase(const sp<IBinder>& processToken, uint64_t id) { - if (!processToken) { - ALOGE("failed to uncache buffer: invalid process token"); - return; - } - - std::lock_guard lock(mMutex); - - if (mBuffers.find(processToken) == mBuffers.end()) { - ALOGE("failed to uncache buffer: process token not found"); - return; - } - - auto& processBuffers = mBuffers[processToken]; - processBuffers.erase(id); -} - -sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, uint64_t id) { - if (!processToken) { - ALOGE("failed to cache buffer: invalid process token"); - return nullptr; - } - - std::lock_guard lock(mMutex); - auto itr = mBuffers.find(processToken); - if (itr == mBuffers.end()) { - ALOGE("failed to get buffer: process token not found"); - return nullptr; - } - - if (itr->second.find(id) == itr->second.end()) { - ALOGE("failed to get buffer: buffer not found"); - return nullptr; - } - - return itr->second[id]; -} - -void BufferStateLayerCache::removeProcess(const wp<IBinder>& processToken) { - std::lock_guard lock(mMutex); - mBuffers.erase(processToken); -} - -void BufferStateLayerCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) { - BufferStateLayerCache::getInstance().removeProcess(who); -} - -}; // namespace android diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp new file mode 100644 index 0000000000..77f2f5765c --- /dev/null +++ b/services/surfaceflinger/ClientCache.cpp @@ -0,0 +1,202 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "ClientCache" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <cinttypes> + +#include "ClientCache.h" + +namespace android { + +ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache); + +ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {} + +bool ClientCache::getBuffer(const client_cache_t& cacheId, + ClientCacheBuffer** outClientCacheBuffer) { + auto& [processToken, id] = cacheId; + if (processToken == nullptr) { + ALOGE("failed to get buffer, invalid (nullptr) process token"); + return false; + } + auto it = mBuffers.find(processToken); + if (it == mBuffers.end()) { + ALOGE("failed to get buffer, invalid process token"); + return false; + } + + auto& processBuffers = it->second; + + auto bufItr = processBuffers.find(id); + if (bufItr == processBuffers.end()) { + ALOGE("failed to get buffer, invalid buffer id"); + return false; + } + + ClientCacheBuffer& buf = bufItr->second; + *outClientCacheBuffer = &buf; + return true; +} + +void ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) { + auto& [processToken, id] = cacheId; + if (processToken == nullptr) { + ALOGE("failed to cache buffer: invalid process token"); + return; + } + + if (!buffer) { + ALOGE("failed to cache buffer: invalid buffer"); + return; + } + + std::lock_guard lock(mMutex); + sp<IBinder> token; + + // If this is a new process token, set a death recipient. If the client process dies, we will + // get a callback through binderDied. + auto it = mBuffers.find(processToken); + if (it == mBuffers.end()) { + token = processToken.promote(); + if (!token) { + ALOGE("failed to cache buffer: invalid token"); + return; + } + + status_t err = token->linkToDeath(mDeathRecipient); + if (err != NO_ERROR) { + ALOGE("failed to cache buffer: could not link to death"); + return; + } + auto [itr, success] = + mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>()); + LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache"); + it = itr; + } + + auto& processBuffers = it->second; + + if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) { + ALOGE("failed to cache buffer: cache is full"); + return; + } + + processBuffers[id].buffer = buffer; +} + +void ClientCache::erase(const client_cache_t& cacheId) { + auto& [processToken, id] = cacheId; + std::vector<sp<ErasedRecipient>> pendingErase; + { + std::lock_guard lock(mMutex); + ClientCacheBuffer* buf = nullptr; + if (!getBuffer(cacheId, &buf)) { + ALOGE("failed to erase buffer, could not retrieve buffer"); + return; + } + + for (auto& recipient : buf->recipients) { + sp<ErasedRecipient> erasedRecipient = recipient.promote(); + if (erasedRecipient) { + pendingErase.push_back(erasedRecipient); + } + } + + mBuffers[processToken].erase(id); + } + + for (auto& recipient : pendingErase) { + recipient->bufferErased(cacheId); + } +} + +sp<GraphicBuffer> ClientCache::get(const client_cache_t& cacheId) { + std::lock_guard lock(mMutex); + + ClientCacheBuffer* buf = nullptr; + if (!getBuffer(cacheId, &buf)) { + ALOGE("failed to get buffer, could not retrieve buffer"); + return nullptr; + } + + return buf->buffer; +} + +void ClientCache::registerErasedRecipient(const client_cache_t& cacheId, + const wp<ErasedRecipient>& recipient) { + std::lock_guard lock(mMutex); + + ClientCacheBuffer* buf = nullptr; + if (!getBuffer(cacheId, &buf)) { + ALOGE("failed to register erased recipient, could not retrieve buffer"); + return; + } + buf->recipients.insert(recipient); +} + +void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId, + const wp<ErasedRecipient>& recipient) { + std::lock_guard lock(mMutex); + + ClientCacheBuffer* buf = nullptr; + if (!getBuffer(cacheId, &buf)) { + ALOGE("failed to unregister erased recipient"); + return; + } + + buf->recipients.erase(recipient); +} + +void ClientCache::removeProcess(const wp<IBinder>& processToken) { + std::vector<std::pair<sp<ErasedRecipient>, client_cache_t>> pendingErase; + { + if (processToken == nullptr) { + ALOGE("failed to remove process, invalid (nullptr) process token"); + return; + } + std::lock_guard lock(mMutex); + auto itr = mBuffers.find(processToken); + if (itr == mBuffers.end()) { + ALOGE("failed to remove process, could not find process"); + return; + } + + for (auto& [id, clientCacheBuffer] : itr->second) { + client_cache_t cacheId = {processToken, id}; + for (auto& recipient : clientCacheBuffer.recipients) { + sp<ErasedRecipient> erasedRecipient = recipient.promote(); + if (erasedRecipient) { + pendingErase.emplace_back(erasedRecipient, cacheId); + } + } + } + mBuffers.erase(itr); + } + + for (auto& [recipient, cacheId] : pendingErase) { + recipient->bufferErased(cacheId); + } +} + +void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) { + ClientCache::getInstance().removeProcess(who); +} + +}; // namespace android diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/ClientCache.h index 415c09cf96..9f057c45d4 100644 --- a/services/surfaceflinger/BufferStateLayerCache.h +++ b/services/surfaceflinger/ClientCache.h @@ -18,32 +18,50 @@ #include <android-base/thread_annotations.h> #include <binder/IBinder.h> +#include <gui/LayerState.h> #include <ui/GraphicBuffer.h> #include <utils/RefBase.h> #include <utils/Singleton.h> -#include <array> #include <map> #include <mutex> +#include <set> +#include <unordered_map> #define BUFFER_CACHE_MAX_SIZE 64 namespace android { -class BufferStateLayerCache : public Singleton<BufferStateLayerCache> { +class ClientCache : public Singleton<ClientCache> { public: - BufferStateLayerCache(); + ClientCache(); - void add(const sp<IBinder>& processToken, uint64_t id, const sp<GraphicBuffer>& buffer); - void erase(const sp<IBinder>& processToken, uint64_t id); + void add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer); + void erase(const client_cache_t& cacheId); - sp<GraphicBuffer> get(const sp<IBinder>& processToken, uint64_t id); + sp<GraphicBuffer> get(const client_cache_t& cacheId); void removeProcess(const wp<IBinder>& processToken); + class ErasedRecipient : public virtual RefBase { + public: + virtual void bufferErased(const client_cache_t& clientCacheId) = 0; + }; + + void registerErasedRecipient(const client_cache_t& cacheId, + const wp<ErasedRecipient>& recipient); + void unregisterErasedRecipient(const client_cache_t& cacheId, + const wp<ErasedRecipient>& recipient); + private: std::mutex mMutex; - std::map<wp<IBinder> /*caching process*/, std::map<uint64_t /*Cache id*/, sp<GraphicBuffer>>> + + struct ClientCacheBuffer { + sp<GraphicBuffer> buffer; + std::set<wp<ErasedRecipient>> recipients; + }; + std::map<wp<IBinder> /*caching process*/, + std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>> mBuffers GUARDED_BY(mMutex); class CacheDeathRecipient : public IBinder::DeathRecipient { @@ -52,6 +70,9 @@ private: }; sp<CacheDeathRecipient> mDeathRecipient; + + bool getBuffer(const client_cache_t& cacheId, ClientCacheBuffer** outClientCacheBuffer) + REQUIRES(mMutex); }; }; // namespace android diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h index 97bdc8ff67..8eec035bd9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h @@ -48,20 +48,11 @@ public: void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot, sp<GraphicBuffer>* outBuffer); -protected: - bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot); - uint32_t getLeastRecentlyUsedSlot(); - uint64_t getCounter(); - private: // 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; + wp<GraphicBuffer> mBuffers[BufferQueue::NUM_BUFFER_SLOTS]; }; } // namespace compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp index a941e0962d..f72862be07 100644 --- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp @@ -21,60 +21,30 @@ namespace android::compositionengine::impl { HwcBufferCache::HwcBufferCache() { - std::fill(std::begin(mBuffers), std::end(mBuffers), - std::pair<uint64_t, wp<GraphicBuffer>>(0, nullptr)); -} -bool HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) { - // search for cached buffer first - for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - // Weak pointers in the cache may have had their object destroyed. - // Comparisons between weak pointers will accurately reflect this case, - // but comparisons between weak and strong may not. Thus, we create a weak - // pointer from strong pointer buffer - wp<GraphicBuffer> weakCopy(buffer); - if (mBuffers[i].second == weakCopy) { - *outSlot = i; - return true; - } - } - - // use the least-recently used slot - *outSlot = getLeastRecentlyUsedSlot(); - return false; -} - -uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() { - auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers)); - return std::distance(std::begin(mBuffers), iter); + std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr)); } void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot, sp<GraphicBuffer>* outBuffer) { - // if this slot corresponds to a BufferStateLayer, generate the slot - if (slot == BufferQueue::INVALID_BUFFER_SLOT) { - getSlot(buffer, outSlot); - } else if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { + // default is 0 + if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 || + slot >= BufferQueue::NUM_BUFFER_SLOTS) { *outSlot = 0; } else { *outSlot = slot; } - auto& [currentCounter, currentBuffer] = mBuffers[*outSlot]; + auto& currentBuffer = mBuffers[*outSlot]; wp<GraphicBuffer> weakCopy(buffer); if (currentBuffer == weakCopy) { // already cached in HWC, skip sending the buffer *outBuffer = nullptr; - currentCounter = getCounter(); } else { *outBuffer = buffer; // update cache currentBuffer = buffer; - currentCounter = getCounter(); } } -uint64_t HwcBufferCache::getCounter() { - return mCounter++; -} } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp index b261493a20..00eafb15f5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp @@ -28,10 +28,6 @@ public: sp<GraphicBuffer>* outBuffer) { HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer); } - bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) { - return HwcBufferCache::getSlot(buffer, outSlot); - } - uint32_t getLeastRecentlyUsedSlot() { return HwcBufferCache::getLeastRecentlyUsedSlot(); } }; class HwcBufferCacheTest : public testing::Test { @@ -86,41 +82,5 @@ TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) { testSlot(-123, 0); } -TEST_F(HwcBufferCacheTest, cacheGeneratesSlotForInvalidBufferSlot) { - uint32_t outSlot; - sp<GraphicBuffer> outBuffer; - - mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer); - EXPECT_EQ(0, outSlot); - EXPECT_EQ(mBuffer1, outBuffer); - - mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer); - EXPECT_EQ(0, outSlot); - EXPECT_EQ(nullptr, outBuffer.get()); - - mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer); - EXPECT_EQ(1, outSlot); - EXPECT_EQ(mBuffer2, outBuffer); - - mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer); - EXPECT_EQ(1, outSlot); - EXPECT_EQ(nullptr, outBuffer.get()); - - mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, sp<GraphicBuffer>(), &outSlot, - &outBuffer); - EXPECT_EQ(2, outSlot); - EXPECT_EQ(nullptr, outBuffer.get()); - - // note that sending mBuffer1 with explicit slot 1 will overwrite mBuffer2 - // and also cause mBuffer1 to be stored in two places - mCache.getHwcBuffer(1, mBuffer1, &outSlot, &outBuffer); - EXPECT_EQ(1, outSlot); - EXPECT_EQ(mBuffer1, outBuffer); - - mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer); - EXPECT_EQ(3, outSlot); - EXPECT_EQ(mBuffer2, outBuffer); -} - } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index f4545e0538..c76394cf31 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -199,6 +199,7 @@ public: Region transparentRegionHint; sp<GraphicBuffer> buffer; + client_cache_t clientCacheId; sp<Fence> acquireFence; HdrMetadata hdrMetadata; Region surfaceDamageRegion; @@ -312,9 +313,10 @@ public: virtual bool setCrop(const Rect& /*crop*/) { return false; }; virtual bool setFrame(const Rect& /*frame*/) { return false; }; virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, nsecs_t /*postTime*/, - nsecs_t /*desiredPresentTime*/) { + nsecs_t /*desiredPresentTime*/, + const client_cache_t& /*clientCacheId*/) { return false; - } + }; virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; }; virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; }; virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 606355549e..29441d95d6 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -75,7 +75,6 @@ #include "BufferLayer.h" #include "BufferQueueLayer.h" #include "BufferStateLayer.h" -#include "BufferStateLayerCache.h" #include "Client.h" #include "ColorLayer.h" #include "Colorizer.h" @@ -3542,7 +3541,7 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, - const cached_buffer_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, const std::vector<ListenerCallbacks>& listenerCallbacks) { ATRACE_CALL(); @@ -3574,7 +3573,7 @@ void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, - const cached_buffer_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, const std::vector<ListenerCallbacks>& listenerCallbacks, const int64_t postTime, bool privileged, bool isMainThread) { @@ -3624,8 +3623,8 @@ void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, transactionFlags |= addInputWindowCommands(inputWindowCommands); - if (uncacheBuffer.token) { - BufferStateLayerCache::getInstance().erase(uncacheBuffer.token, uncacheBuffer.cacheId); + if (uncacheBuffer.isValid()) { + ClientCache::getInstance().erase(uncacheBuffer); } // If a synchronous transaction is explicitly requested without any changes, force a transaction @@ -3980,17 +3979,15 @@ uint32_t SurfaceFlinger::setClientStateLocked( bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged; sp<GraphicBuffer> buffer; if (bufferChanged && cacheIdChanged) { - BufferStateLayerCache::getInstance().add(s.cachedBuffer.token, s.cachedBuffer.cacheId, - s.buffer); + ClientCache::getInstance().add(s.cachedBuffer, s.buffer); buffer = s.buffer; } else if (cacheIdChanged) { - buffer = BufferStateLayerCache::getInstance().get(s.cachedBuffer.token, - s.cachedBuffer.cacheId); + buffer = ClientCache::getInstance().get(s.cachedBuffer); } else if (bufferChanged) { buffer = s.buffer; } if (buffer) { - if (layer->setBuffer(buffer, postTime, desiredPresentTime)) { + if (layer->setBuffer(buffer, postTime, desiredPresentTime, s.cachedBuffer)) { flags |= eTraversalNeeded; } } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 7a7ad33d67..72b47211b5 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -46,6 +46,7 @@ #include <utils/Trace.h> #include <utils/threads.h> +#include "ClientCache.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/PowerAdvisor.h" @@ -386,7 +387,7 @@ private: const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, - int64_t desiredPresentTime, const cached_buffer_t& uncacheBuffer, + int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, const std::vector<ListenerCallbacks>& listenerCallbacks) override; void bootFinished() override; bool authenticateSurfaceTexture( @@ -545,7 +546,7 @@ private: const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, - const cached_buffer_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, const std::vector<ListenerCallbacks>& listenerCallbacks, const int64_t postTime, bool privileged, bool isMainThread = false) REQUIRES(mStateLock); @@ -1031,7 +1032,7 @@ private: struct TransactionState { TransactionState(const Vector<ComposerState>& composerStates, const Vector<DisplayState>& displayStates, uint32_t transactionFlags, - int64_t desiredPresentTime, const cached_buffer_t& uncacheBuffer, + int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime, bool privileged) : states(composerStates), @@ -1047,7 +1048,7 @@ private: Vector<DisplayState> displays; uint32_t flags; const int64_t desiredPresentTime; - cached_buffer_t buffer; + client_cache_t buffer; std::vector<ListenerCallbacks> callback; const int64_t postTime; bool privileged; |