1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #undef LOG_TAG
19 #define LOG_TAG "ClientCache"
20 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
21
22 #include <cinttypes>
23
24 #include "ClientCache.h"
25
26 namespace android {
27
28 using base::StringAppendF;
29
30 ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
31
ClientCache()32 ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
33
getBuffer(const client_cache_t & cacheId,ClientCacheBuffer ** outClientCacheBuffer)34 bool ClientCache::getBuffer(const client_cache_t& cacheId,
35 ClientCacheBuffer** outClientCacheBuffer) {
36 auto& [processToken, id] = cacheId;
37 if (processToken == nullptr) {
38 ALOGE("failed to get buffer, invalid (nullptr) process token");
39 return false;
40 }
41 auto it = mBuffers.find(processToken);
42 if (it == mBuffers.end()) {
43 ALOGE("failed to get buffer, invalid process token");
44 return false;
45 }
46
47 auto& processBuffers = it->second.second;
48
49 auto bufItr = processBuffers.find(id);
50 if (bufItr == processBuffers.end()) {
51 ALOGV("failed to get buffer, invalid buffer id");
52 return false;
53 }
54
55 ClientCacheBuffer& buf = bufItr->second;
56 *outClientCacheBuffer = &buf;
57 return true;
58 }
59
add(const client_cache_t & cacheId,const sp<GraphicBuffer> & buffer)60 bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
61 auto& [processToken, id] = cacheId;
62 if (processToken == nullptr) {
63 ALOGE("failed to cache buffer: invalid process token");
64 return false;
65 }
66
67 if (!buffer) {
68 ALOGE("failed to cache buffer: invalid buffer");
69 return false;
70 }
71
72 std::lock_guard lock(mMutex);
73 sp<IBinder> token;
74
75 // If this is a new process token, set a death recipient. If the client process dies, we will
76 // get a callback through binderDied.
77 auto it = mBuffers.find(processToken);
78 if (it == mBuffers.end()) {
79 token = processToken.promote();
80 if (!token) {
81 ALOGE("failed to cache buffer: invalid token");
82 return false;
83 }
84
85 status_t err = token->linkToDeath(mDeathRecipient);
86 if (err != NO_ERROR) {
87 ALOGE("failed to cache buffer: could not link to death");
88 return false;
89 }
90 auto [itr, success] =
91 mBuffers.emplace(processToken,
92 std::make_pair(token,
93 std::unordered_map<uint64_t, ClientCacheBuffer>()));
94 LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
95 it = itr;
96 }
97
98 auto& processBuffers = it->second.second;
99
100 if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
101 ALOGE("failed to cache buffer: cache is full");
102 return false;
103 }
104
105 LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
106 "Attempted to build the ClientCache before a RenderEngine instance was "
107 "ready!");
108 processBuffers[id].buffer = std::make_shared<
109 renderengine::ExternalTexture>(buffer, *mRenderEngine,
110 renderengine::ExternalTexture::Usage::READABLE);
111 return true;
112 }
113
erase(const client_cache_t & cacheId)114 void ClientCache::erase(const client_cache_t& cacheId) {
115 auto& [processToken, id] = cacheId;
116 std::vector<sp<ErasedRecipient>> pendingErase;
117 {
118 std::lock_guard lock(mMutex);
119 ClientCacheBuffer* buf = nullptr;
120 if (!getBuffer(cacheId, &buf)) {
121 ALOGE("failed to erase buffer, could not retrieve buffer");
122 return;
123 }
124
125 for (auto& recipient : buf->recipients) {
126 sp<ErasedRecipient> erasedRecipient = recipient.promote();
127 if (erasedRecipient) {
128 pendingErase.push_back(erasedRecipient);
129 }
130 }
131
132 mBuffers[processToken].second.erase(id);
133 }
134
135 for (auto& recipient : pendingErase) {
136 recipient->bufferErased(cacheId);
137 }
138 }
139
get(const client_cache_t & cacheId)140 std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
141 std::lock_guard lock(mMutex);
142
143 ClientCacheBuffer* buf = nullptr;
144 if (!getBuffer(cacheId, &buf)) {
145 ALOGE("failed to get buffer, could not retrieve buffer");
146 return nullptr;
147 }
148
149 return buf->buffer;
150 }
151
registerErasedRecipient(const client_cache_t & cacheId,const wp<ErasedRecipient> & recipient)152 bool ClientCache::registerErasedRecipient(const client_cache_t& cacheId,
153 const wp<ErasedRecipient>& recipient) {
154 std::lock_guard lock(mMutex);
155
156 ClientCacheBuffer* buf = nullptr;
157 if (!getBuffer(cacheId, &buf)) {
158 ALOGV("failed to register erased recipient, could not retrieve buffer");
159 return false;
160 }
161 buf->recipients.insert(recipient);
162 return true;
163 }
164
unregisterErasedRecipient(const client_cache_t & cacheId,const wp<ErasedRecipient> & recipient)165 void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId,
166 const wp<ErasedRecipient>& recipient) {
167 std::lock_guard lock(mMutex);
168
169 ClientCacheBuffer* buf = nullptr;
170 if (!getBuffer(cacheId, &buf)) {
171 ALOGE("failed to unregister erased recipient");
172 return;
173 }
174
175 buf->recipients.erase(recipient);
176 }
177
removeProcess(const wp<IBinder> & processToken)178 void ClientCache::removeProcess(const wp<IBinder>& processToken) {
179 std::vector<std::pair<sp<ErasedRecipient>, client_cache_t>> pendingErase;
180 {
181 if (processToken == nullptr) {
182 ALOGE("failed to remove process, invalid (nullptr) process token");
183 return;
184 }
185 std::lock_guard lock(mMutex);
186 auto itr = mBuffers.find(processToken);
187 if (itr == mBuffers.end()) {
188 ALOGE("failed to remove process, could not find process");
189 return;
190 }
191
192 for (auto& [id, clientCacheBuffer] : itr->second.second) {
193 client_cache_t cacheId = {processToken, id};
194 for (auto& recipient : clientCacheBuffer.recipients) {
195 sp<ErasedRecipient> erasedRecipient = recipient.promote();
196 if (erasedRecipient) {
197 pendingErase.emplace_back(erasedRecipient, cacheId);
198 }
199 }
200 }
201 mBuffers.erase(itr);
202 }
203
204 for (auto& [recipient, cacheId] : pendingErase) {
205 recipient->bufferErased(cacheId);
206 }
207 }
208
binderDied(const wp<IBinder> & who)209 void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
210 ClientCache::getInstance().removeProcess(who);
211 }
212
dump(std::string & result)213 void ClientCache::dump(std::string& result) {
214 std::lock_guard lock(mMutex);
215 for (auto i : mBuffers) {
216 const sp<IBinder>& cacheOwner = i.second.first;
217 StringAppendF(&result," Cache owner: %p\n", cacheOwner.get());
218 auto &buffers = i.second.second;
219 for (auto& [id, clientCacheBuffer] : buffers) {
220 StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id,
221 (int)clientCacheBuffer.buffer->getBuffer()->getWidth(),
222 (int)clientCacheBuffer.buffer->getBuffer()->getHeight());
223 }
224 }
225 }
226
227 }; // namespace android
228