• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android-base/stringprintf.h>
25 #include <renderengine/impl/ExternalTexture.h>
26 
27 #include "ClientCache.h"
28 
29 namespace android {
30 
31 ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
32 
ClientCache()33 ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
34 
getBuffer(const client_cache_t & cacheId,ClientCacheBuffer ** outClientCacheBuffer)35 bool ClientCache::getBuffer(const client_cache_t& cacheId,
36                             ClientCacheBuffer** outClientCacheBuffer) {
37     auto& [processToken, id] = cacheId;
38     if (processToken == nullptr) {
39         ALOGE("failed to get buffer, invalid (nullptr) process token");
40         return false;
41     }
42     auto it = mBuffers.find(processToken);
43     if (it == mBuffers.end()) {
44         ALOGE("failed to get buffer, invalid process token");
45         return false;
46     }
47 
48     auto& processBuffers = it->second.second;
49 
50     auto bufItr = processBuffers.find(id);
51     if (bufItr == processBuffers.end()) {
52         ALOGV("failed to get buffer, invalid buffer id");
53         return false;
54     }
55 
56     ClientCacheBuffer& buf = bufItr->second;
57     *outClientCacheBuffer = &buf;
58     return true;
59 }
60 
add(const client_cache_t & cacheId,const sp<GraphicBuffer> & buffer)61 bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
62     auto& [processToken, id] = cacheId;
63     if (processToken == nullptr) {
64         ALOGE("failed to cache buffer: invalid process token");
65         return false;
66     }
67 
68     if (!buffer) {
69         ALOGE("failed to cache buffer: invalid buffer");
70         return false;
71     }
72 
73     std::lock_guard lock(mMutex);
74     sp<IBinder> token;
75 
76     // If this is a new process token, set a death recipient. If the client process dies, we will
77     // get a callback through binderDied.
78     auto it = mBuffers.find(processToken);
79     if (it == mBuffers.end()) {
80         token = processToken.promote();
81         if (!token) {
82             ALOGE("failed to cache buffer: invalid token");
83             return false;
84         }
85 
86         // Only call linkToDeath if not a local binder
87         if (token->localBinder() == nullptr) {
88             status_t err = token->linkToDeath(mDeathRecipient);
89             if (err != NO_ERROR) {
90                 ALOGE("failed to cache buffer: could not link to death");
91                 return false;
92             }
93         }
94         auto [itr, success] =
95                 mBuffers.emplace(processToken,
96                                  std::make_pair(token,
97                                                 std::unordered_map<uint64_t, ClientCacheBuffer>()));
98         LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
99         it = itr;
100     }
101 
102     auto& processBuffers = it->second.second;
103 
104     if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
105         ALOGE("failed to cache buffer: cache is full");
106         return false;
107     }
108 
109     LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
110                         "Attempted to build the ClientCache before a RenderEngine instance was "
111                         "ready!");
112     processBuffers[id].buffer = std::make_shared<
113             renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
114                                                  renderengine::impl::ExternalTexture::Usage::
115                                                          READABLE);
116     return true;
117 }
118 
erase(const client_cache_t & cacheId)119 void ClientCache::erase(const client_cache_t& cacheId) {
120     auto& [processToken, id] = cacheId;
121     std::vector<sp<ErasedRecipient>> pendingErase;
122     {
123         std::lock_guard lock(mMutex);
124         ClientCacheBuffer* buf = nullptr;
125         if (!getBuffer(cacheId, &buf)) {
126             ALOGE("failed to erase buffer, could not retrieve buffer");
127             return;
128         }
129 
130         for (auto& recipient : buf->recipients) {
131             sp<ErasedRecipient> erasedRecipient = recipient.promote();
132             if (erasedRecipient) {
133                 pendingErase.push_back(erasedRecipient);
134             }
135         }
136 
137         mBuffers[processToken].second.erase(id);
138     }
139 
140     for (auto& recipient : pendingErase) {
141         recipient->bufferErased(cacheId);
142     }
143 }
144 
get(const client_cache_t & cacheId)145 std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
146     std::lock_guard lock(mMutex);
147 
148     ClientCacheBuffer* buf = nullptr;
149     if (!getBuffer(cacheId, &buf)) {
150         ALOGE("failed to get buffer, could not retrieve buffer");
151         return nullptr;
152     }
153 
154     return buf->buffer;
155 }
156 
registerErasedRecipient(const client_cache_t & cacheId,const wp<ErasedRecipient> & recipient)157 bool ClientCache::registerErasedRecipient(const client_cache_t& cacheId,
158                                           const wp<ErasedRecipient>& recipient) {
159     std::lock_guard lock(mMutex);
160 
161     ClientCacheBuffer* buf = nullptr;
162     if (!getBuffer(cacheId, &buf)) {
163         ALOGV("failed to register erased recipient, could not retrieve buffer");
164         return false;
165     }
166     buf->recipients.insert(recipient);
167     return true;
168 }
169 
unregisterErasedRecipient(const client_cache_t & cacheId,const wp<ErasedRecipient> & recipient)170 void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId,
171                                             const wp<ErasedRecipient>& recipient) {
172     std::lock_guard lock(mMutex);
173 
174     ClientCacheBuffer* buf = nullptr;
175     if (!getBuffer(cacheId, &buf)) {
176         ALOGE("failed to unregister erased recipient");
177         return;
178     }
179 
180     buf->recipients.erase(recipient);
181 }
182 
removeProcess(const wp<IBinder> & processToken)183 void ClientCache::removeProcess(const wp<IBinder>& processToken) {
184     std::vector<std::pair<sp<ErasedRecipient>, client_cache_t>> pendingErase;
185     {
186         if (processToken == nullptr) {
187             ALOGE("failed to remove process, invalid (nullptr) process token");
188             return;
189         }
190         std::lock_guard lock(mMutex);
191         auto itr = mBuffers.find(processToken);
192         if (itr == mBuffers.end()) {
193             ALOGE("failed to remove process, could not find process");
194             return;
195         }
196 
197         for (auto& [id, clientCacheBuffer] : itr->second.second) {
198             client_cache_t cacheId = {processToken, id};
199             for (auto& recipient : clientCacheBuffer.recipients) {
200                 sp<ErasedRecipient> erasedRecipient = recipient.promote();
201                 if (erasedRecipient) {
202                     pendingErase.emplace_back(erasedRecipient, cacheId);
203                 }
204             }
205         }
206         mBuffers.erase(itr);
207     }
208 
209     for (auto& [recipient, cacheId] : pendingErase) {
210         recipient->bufferErased(cacheId);
211     }
212 }
213 
binderDied(const wp<IBinder> & who)214 void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
215     ClientCache::getInstance().removeProcess(who);
216 }
217 
dump(std::string & result)218 void ClientCache::dump(std::string& result) {
219     std::lock_guard lock(mMutex);
220     for (const auto& [_, cache] : mBuffers) {
221         base::StringAppendF(&result, " Cache owner: %p\n", cache.first.get());
222 
223         for (const auto& [id, entry] : cache.second) {
224             const auto& buffer = entry.buffer->getBuffer();
225             base::StringAppendF(&result, "\tID: %" PRIu64 ", size: %ux%u\n", id, buffer->getWidth(),
226                                 buffer->getHeight());
227         }
228     }
229 }
230 
231 } // namespace android
232