• 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 "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