blob: e46716c9b7eca1aaa6e8f1cee0b8aa3acf2bfc61 [file] [log] [blame]
Jamie Gennisaca51c02011-11-03 17:42:43 -07001/*
2 ** Copyright 2011, 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
Mathias Agopian311b4792017-02-28 15:00:49 -080017#include "egl_cache.h"
18
Mathias Agopian39c24a22013-04-04 23:17:56 -070019#include "../egl_impl.h"
20
Jamie Gennisaca51c02011-11-03 17:42:43 -070021#include "egl_display.h"
Jamie Gennisaca51c02011-11-03 17:42:43 -070022
Dan Alberteacd31f2016-02-02 15:08:34 -080023#include <inttypes.h>
Jamie Gennis98c63832011-11-07 17:03:54 -080024#include <sys/mman.h>
25#include <sys/stat.h>
Mathias Agopian311b4792017-02-28 15:00:49 -080026
27#include <utils/Thread.h>
Jamie Gennis98c63832011-11-07 17:03:54 -080028
Jamie Gennis76601082011-11-06 14:14:33 -080029// Cache size limits.
Dan Willemsenbace39e2016-10-11 15:42:59 -070030static const size_t maxKeySize = 12 * 1024;
31static const size_t maxValueSize = 64 * 1024;
32static const size_t maxTotalSize = 2 * 1024 * 1024;
Jamie Gennis76601082011-11-06 14:14:33 -080033
Jamie Gennis98c63832011-11-07 17:03:54 -080034// Cache file header
35static const char* cacheFileMagic = "EGL$";
36static const size_t cacheFileHeaderSize = 8;
37
Jamie Gennis99c3d702011-11-08 17:59:36 -080038// The time in seconds to wait before saving newly inserted cache entries.
39static const unsigned int deferredSaveDelay = 4;
40
Jamie Gennisaca51c02011-11-03 17:42:43 -070041// ----------------------------------------------------------------------------
42namespace android {
43// ----------------------------------------------------------------------------
44
45#define BC_EXT_STR "EGL_ANDROID_blob_cache"
46
47//
Jamie Gennis76601082011-11-06 14:14:33 -080048// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070049//
Jamie Gennisc42fcf02011-11-09 15:35:34 -080050static void setBlob(const void* key, EGLsizeiANDROID keySize,
51 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080052 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070053}
54
Jamie Gennisc42fcf02011-11-09 15:35:34 -080055static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
56 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080057 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
58}
59
60//
61// egl_cache_t definition
62//
63egl_cache_t::egl_cache_t() :
64 mInitialized(false),
65 mBlobCache(NULL) {
66}
67
68egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070069}
70
Jamie Gennis98c63832011-11-07 17:03:54 -080071egl_cache_t egl_cache_t::sCache;
72
Jamie Gennisaca51c02011-11-03 17:42:43 -070073egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080074 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070075}
76
77void egl_cache_t::initialize(egl_display_t *display) {
Jamie Gennis76601082011-11-06 14:14:33 -080078 Mutex::Autolock lock(mMutex);
Mathias Agopianada798b2012-02-13 17:09:30 -080079
80 egl_connection_t* const cnx = &gEGLImpl;
81 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
82 const char* exts = display->disp.queryString.extensions;
83 size_t bcExtLen = strlen(BC_EXT_STR);
84 size_t extsLen = strlen(exts);
85 bool equal = !strcmp(BC_EXT_STR, exts);
86 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
87 bool atEnd = (bcExtLen+1) < extsLen &&
88 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
Mathias Agopian311b4792017-02-28 15:00:49 -080089 bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
Mathias Agopianada798b2012-02-13 17:09:30 -080090 if (equal || atStart || atEnd || inMiddle) {
91 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
92 eglSetBlobCacheFuncsANDROID =
93 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
Jamie Gennisc42fcf02011-11-09 15:35:34 -080094 cnx->egl.eglGetProcAddress(
95 "eglSetBlobCacheFuncsANDROID"));
Mathias Agopianada798b2012-02-13 17:09:30 -080096 if (eglSetBlobCacheFuncsANDROID == NULL) {
97 ALOGE("EGL_ANDROID_blob_cache advertised, "
98 "but unable to get eglSetBlobCacheFuncsANDROID");
99 return;
100 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700101
Mathias Agopianada798b2012-02-13 17:09:30 -0800102 eglSetBlobCacheFuncsANDROID(display->disp.dpy,
103 android::setBlob, android::getBlob);
104 EGLint err = cnx->egl.eglGetError();
105 if (err != EGL_SUCCESS) {
106 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
107 "%#x", err);
Jamie Gennisaca51c02011-11-03 17:42:43 -0700108 }
109 }
110 }
Mathias Agopianada798b2012-02-13 17:09:30 -0800111
Jamie Gennis76601082011-11-06 14:14:33 -0800112 mInitialized = true;
113}
114
115void egl_cache_t::terminate() {
116 Mutex::Autolock lock(mMutex);
Jamie Gennis5539e212013-07-30 15:10:02 -0700117 saveBlobCacheLocked();
118 mBlobCache = NULL;
Jamie Gennis76601082011-11-06 14:14:33 -0800119}
120
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800121void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
122 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800123 Mutex::Autolock lock(mMutex);
124
125 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000126 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800127 return;
128 }
129
130 if (mInitialized) {
131 sp<BlobCache> bc = getBlobCacheLocked();
132 bc->set(key, keySize, value, valueSize);
Jamie Gennis99c3d702011-11-08 17:59:36 -0800133
134 if (!mSavePending) {
135 class DeferredSaveThread : public Thread {
136 public:
137 DeferredSaveThread() : Thread(false) {}
138
139 virtual bool threadLoop() {
140 sleep(deferredSaveDelay);
141 egl_cache_t* c = egl_cache_t::get();
142 Mutex::Autolock lock(c->mMutex);
143 if (c->mInitialized) {
144 c->saveBlobCacheLocked();
145 }
146 c->mSavePending = false;
147 return false;
148 }
149 };
150
151 // The thread will hold a strong ref to itself until it has finished
152 // running, so there's no need to keep a ref around.
153 sp<Thread> deferredSaveThread(new DeferredSaveThread());
154 mSavePending = true;
Brian Carlstrom83b1e682016-03-12 16:07:59 -0800155 deferredSaveThread->run("DeferredSaveThread");
Jamie Gennis99c3d702011-11-08 17:59:36 -0800156 }
Jamie Gennis76601082011-11-06 14:14:33 -0800157 }
158}
159
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800160EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
161 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800162 Mutex::Autolock lock(mMutex);
163
164 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000165 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800166 return 0;
167 }
168
169 if (mInitialized) {
170 sp<BlobCache> bc = getBlobCacheLocked();
171 return bc->get(key, keySize, value, valueSize);
172 }
173 return 0;
174}
175
Jamie Gennis98c63832011-11-07 17:03:54 -0800176void egl_cache_t::setCacheFilename(const char* filename) {
177 Mutex::Autolock lock(mMutex);
178 mFilename = filename;
179}
180
Jamie Gennis76601082011-11-06 14:14:33 -0800181sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
182 if (mBlobCache == NULL) {
183 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
184 loadBlobCacheLocked();
185 }
186 return mBlobCache;
187}
188
Jamie Gennis98c63832011-11-07 17:03:54 -0800189static uint32_t crc32c(const uint8_t* buf, size_t len) {
190 const uint32_t polyBits = 0x82F63B78;
191 uint32_t r = 0;
192 for (size_t i = 0; i < len; i++) {
193 r ^= buf[i];
194 for (int j = 0; j < 8; j++) {
195 if (r & 1) {
196 r = (r >> 1) ^ polyBits;
197 } else {
198 r >>= 1;
199 }
200 }
201 }
202 return r;
203}
204
Jamie Gennis76601082011-11-06 14:14:33 -0800205void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis5539e212013-07-30 15:10:02 -0700206 if (mFilename.length() > 0 && mBlobCache != NULL) {
Jamie Gennis98c63832011-11-07 17:03:54 -0800207 size_t cacheSize = mBlobCache->getFlattenedSize();
208 size_t headerSize = cacheFileHeaderSize;
209 const char* fname = mFilename.string();
210
211 // Try to create the file with no permissions so we can write it
212 // without anyone trying to read it.
213 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
214 if (fd == -1) {
215 if (errno == EEXIST) {
216 // The file exists, delete it and try again.
217 if (unlink(fname) == -1) {
218 // No point in retrying if the unlink failed.
Steve Blocke6f43dd2012-01-06 19:20:56 +0000219 ALOGE("error unlinking cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800220 strerror(errno), errno);
221 return;
222 }
223 // Retry now that we've unlinked the file.
224 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
225 }
226 if (fd == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000227 ALOGE("error creating cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800228 strerror(errno), errno);
229 return;
230 }
231 }
232
233 size_t fileSize = headerSize + cacheSize;
Jamie Gennis98c63832011-11-07 17:03:54 -0800234
vijay guptaa30cc7d2012-06-27 16:14:42 -0700235 uint8_t* buf = new uint8_t [fileSize];
236 if (!buf) {
237 ALOGE("error allocating buffer for cache contents: %s (%d)",
238 strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800239 close(fd);
240 unlink(fname);
241 return;
242 }
243
Mathias Agopiane1424282013-07-29 21:24:40 -0700244 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize);
Jamie Gennis98c63832011-11-07 17:03:54 -0800245 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000246 ALOGE("error writing cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800247 -err);
vijay guptaa30cc7d2012-06-27 16:14:42 -0700248 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800249 close(fd);
250 unlink(fname);
251 return;
252 }
253
254 // Write the file magic and CRC
255 memcpy(buf, cacheFileMagic, 4);
256 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
257 *crc = crc32c(buf + headerSize, cacheSize);
258
vijay guptaa30cc7d2012-06-27 16:14:42 -0700259 if (write(fd, buf, fileSize) == -1) {
260 ALOGE("error writing cache file: %s (%d)", strerror(errno),
261 errno);
262 delete [] buf;
263 close(fd);
264 unlink(fname);
265 return;
266 }
267
268 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800269 fchmod(fd, S_IRUSR);
270 close(fd);
271 }
Jamie Gennis76601082011-11-06 14:14:33 -0800272}
273
274void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800275 if (mFilename.length() > 0) {
276 size_t headerSize = cacheFileHeaderSize;
277
278 int fd = open(mFilename.string(), O_RDONLY, 0);
279 if (fd == -1) {
280 if (errno != ENOENT) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000281 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(),
Jamie Gennis98c63832011-11-07 17:03:54 -0800282 strerror(errno), errno);
283 }
284 return;
285 }
286
287 struct stat statBuf;
288 if (fstat(fd, &statBuf) == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000289 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800290 close(fd);
291 return;
292 }
293
294 // Sanity check the size before trying to mmap it.
295 size_t fileSize = statBuf.st_size;
296 if (fileSize > maxTotalSize * 2) {
Dan Alberteacd31f2016-02-02 15:08:34 -0800297 ALOGE("cache file is too large: %#" PRIx64,
298 static_cast<off64_t>(statBuf.st_size));
Jamie Gennis98c63832011-11-07 17:03:54 -0800299 close(fd);
300 return;
301 }
302
303 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
304 PROT_READ, MAP_PRIVATE, fd, 0));
305 if (buf == MAP_FAILED) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000306 ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
Jamie Gennis98c63832011-11-07 17:03:54 -0800307 errno);
308 close(fd);
309 return;
310 }
311
312 // Check the file magic and CRC
313 size_t cacheSize = fileSize - headerSize;
314 if (memcmp(buf, cacheFileMagic, 4) != 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000315 ALOGE("cache file has bad mojo");
Jamie Gennis98c63832011-11-07 17:03:54 -0800316 close(fd);
317 return;
318 }
319 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
320 if (crc32c(buf + headerSize, cacheSize) != *crc) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000321 ALOGE("cache file failed CRC check");
Jamie Gennis98c63832011-11-07 17:03:54 -0800322 close(fd);
323 return;
324 }
325
Mathias Agopiane1424282013-07-29 21:24:40 -0700326 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize);
Jamie Gennis98c63832011-11-07 17:03:54 -0800327 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000328 ALOGE("error reading cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800329 -err);
330 munmap(buf, fileSize);
331 close(fd);
332 return;
333 }
334
335 munmap(buf, fileSize);
336 close(fd);
337 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700338}
339
340// ----------------------------------------------------------------------------
341}; // namespace android
342// ----------------------------------------------------------------------------