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
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9080d29..4cd0a13 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -116,7 +116,7 @@
"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 2cbb917..6ef4518 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 @@
};
// 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::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 @@
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::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 864a15d..12f0141 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 @@
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 @@
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 51ca45c..0000000
--- 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/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
deleted file mode 100644
index 415c09c..0000000
--- a/services/surfaceflinger/BufferStateLayerCache.h
+++ /dev/null
@@ -1,57 +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.
- */
-
-#pragma once
-
-#include <android-base/thread_annotations.h>
-#include <binder/IBinder.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-
-#include <array>
-#include <map>
-#include <mutex>
-
-#define BUFFER_CACHE_MAX_SIZE 64
-
-namespace android {
-
-class BufferStateLayerCache : public Singleton<BufferStateLayerCache> {
-public:
- BufferStateLayerCache();
-
- void add(const sp<IBinder>& processToken, uint64_t id, const sp<GraphicBuffer>& buffer);
- void erase(const sp<IBinder>& processToken, uint64_t id);
-
- sp<GraphicBuffer> get(const sp<IBinder>& processToken, uint64_t id);
-
- void removeProcess(const wp<IBinder>& processToken);
-
-private:
- std::mutex mMutex;
- std::map<wp<IBinder> /*caching process*/, std::map<uint64_t /*Cache id*/, sp<GraphicBuffer>>>
- mBuffers GUARDED_BY(mMutex);
-
- class CacheDeathRecipient : public IBinder::DeathRecipient {
- public:
- void binderDied(const wp<IBinder>& who) override;
- };
-
- sp<CacheDeathRecipient> mDeathRecipient;
-};
-
-}; // namespace android
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
new file mode 100644
index 0000000..77f2f57
--- /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/ClientCache.h b/services/surfaceflinger/ClientCache.h
new file mode 100644
index 0000000..9f057c4
--- /dev/null
+++ b/services/surfaceflinger/ClientCache.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#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 <map>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+
+#define BUFFER_CACHE_MAX_SIZE 64
+
+namespace android {
+
+class ClientCache : public Singleton<ClientCache> {
+public:
+ ClientCache();
+
+ void add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
+ void erase(const client_cache_t& cacheId);
+
+ 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;
+
+ 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 {
+ public:
+ void binderDied(const wp<IBinder>& who) override;
+ };
+
+ 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 97bdc8f..8eec035 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -48,20 +48,11 @@
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 a941e09..f72862b 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 b261493..00eafb1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -28,10 +28,6 @@
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 @@
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 f4545e0..c76394c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -199,6 +199,7 @@
Region transparentRegionHint;
sp<GraphicBuffer> buffer;
+ client_cache_t clientCacheId;
sp<Fence> acquireFence;
HdrMetadata hdrMetadata;
Region surfaceDamageRegion;
@@ -312,9 +313,10 @@
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 6063555..29441d9 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 @@
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 @@
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 @@
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 @@
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 7a7ad33..72b4721 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 @@
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 @@
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 @@
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 @@
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;