blob: 1fe322dc9fc3c57d9606d837417cd54067cf3901 [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 Agopian39c24a22013-04-04 23:17:56 -070017#include "../egl_impl.h"
18
Jamie Gennisaca51c02011-11-03 17:42:43 -070019#include "egl_cache.h"
20#include "egl_display.h"
Jamie Gennisaca51c02011-11-03 17:42:43 -070021#include "egldefs.h"
22
Jamie Gennis98c63832011-11-07 17:03:54 -080023#include <fcntl.h>
Dan Alberteacd31f2016-02-02 15:08:34 -080024#include <inttypes.h>
Jamie Gennis98c63832011-11-07 17:03:54 -080025#include <sys/mman.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <unistd.h>
29
Jamie Gennis76601082011-11-06 14:14:33 -080030// Cache size limits.
Dan Willemsenbace39e2016-10-11 15:42:59 -070031static const size_t maxKeySize = 12 * 1024;
32static const size_t maxValueSize = 64 * 1024;
33static const size_t maxTotalSize = 2 * 1024 * 1024;
Jamie Gennis76601082011-11-06 14:14:33 -080034
Jamie Gennis98c63832011-11-07 17:03:54 -080035// Cache file header
36static const char* cacheFileMagic = "EGL$";
37static const size_t cacheFileHeaderSize = 8;
38
Jamie Gennis99c3d702011-11-08 17:59:36 -080039// The time in seconds to wait before saving newly inserted cache entries.
40static const unsigned int deferredSaveDelay = 4;
41
Jamie Gennisaca51c02011-11-03 17:42:43 -070042// ----------------------------------------------------------------------------
43namespace android {
44// ----------------------------------------------------------------------------
45
46#define BC_EXT_STR "EGL_ANDROID_blob_cache"
47
48//
Jamie Gennis76601082011-11-06 14:14:33 -080049// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070050//
Jamie Gennisc42fcf02011-11-09 15:35:34 -080051static void setBlob(const void* key, EGLsizeiANDROID keySize,
52 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080053 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070054}
55
Jamie Gennisc42fcf02011-11-09 15:35:34 -080056static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
57 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080058 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
59}
60
61//
62// egl_cache_t definition
63//
64egl_cache_t::egl_cache_t() :
65 mInitialized(false),
66 mBlobCache(NULL) {
67}
68
69egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070070}
71
Jamie Gennis98c63832011-11-07 17:03:54 -080072egl_cache_t egl_cache_t::sCache;
73
Jamie Gennisaca51c02011-11-03 17:42:43 -070074egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080075 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070076}
77
78void egl_cache_t::initialize(egl_display_t *display) {
Jamie Gennis76601082011-11-06 14:14:33 -080079 Mutex::Autolock lock(mMutex);
Mathias Agopianada798b2012-02-13 17:09:30 -080080
81 egl_connection_t* const cnx = &gEGLImpl;
82 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
83 const char* exts = display->disp.queryString.extensions;
84 size_t bcExtLen = strlen(BC_EXT_STR);
85 size_t extsLen = strlen(exts);
86 bool equal = !strcmp(BC_EXT_STR, exts);
87 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
88 bool atEnd = (bcExtLen+1) < extsLen &&
89 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
90 bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
91 if (equal || atStart || atEnd || inMiddle) {
92 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
93 eglSetBlobCacheFuncsANDROID =
94 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
Jamie Gennisc42fcf02011-11-09 15:35:34 -080095 cnx->egl.eglGetProcAddress(
96 "eglSetBlobCacheFuncsANDROID"));
Mathias Agopianada798b2012-02-13 17:09:30 -080097 if (eglSetBlobCacheFuncsANDROID == NULL) {
98 ALOGE("EGL_ANDROID_blob_cache advertised, "
99 "but unable to get eglSetBlobCacheFuncsANDROID");
100 return;
101 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700102
Mathias Agopianada798b2012-02-13 17:09:30 -0800103 eglSetBlobCacheFuncsANDROID(display->disp.dpy,
104 android::setBlob, android::getBlob);
105 EGLint err = cnx->egl.eglGetError();
106 if (err != EGL_SUCCESS) {
107 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
108 "%#x", err);
Jamie Gennisaca51c02011-11-03 17:42:43 -0700109 }
110 }
111 }
Mathias Agopianada798b2012-02-13 17:09:30 -0800112
Jamie Gennis76601082011-11-06 14:14:33 -0800113 mInitialized = true;
114}
115
116void egl_cache_t::terminate() {
117 Mutex::Autolock lock(mMutex);
Jamie Gennis5539e212013-07-30 15:10:02 -0700118 saveBlobCacheLocked();
119 mBlobCache = NULL;
Jamie Gennis76601082011-11-06 14:14:33 -0800120}
121
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800122void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
123 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800124 Mutex::Autolock lock(mMutex);
125
126 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000127 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800128 return;
129 }
130
131 if (mInitialized) {
132 sp<BlobCache> bc = getBlobCacheLocked();
133 bc->set(key, keySize, value, valueSize);
Jamie Gennis99c3d702011-11-08 17:59:36 -0800134
135 if (!mSavePending) {
136 class DeferredSaveThread : public Thread {
137 public:
138 DeferredSaveThread() : Thread(false) {}
139
140 virtual bool threadLoop() {
141 sleep(deferredSaveDelay);
142 egl_cache_t* c = egl_cache_t::get();
143 Mutex::Autolock lock(c->mMutex);
144 if (c->mInitialized) {
145 c->saveBlobCacheLocked();
146 }
147 c->mSavePending = false;
148 return false;
149 }
150 };
151
152 // The thread will hold a strong ref to itself until it has finished
153 // running, so there's no need to keep a ref around.
154 sp<Thread> deferredSaveThread(new DeferredSaveThread());
155 mSavePending = true;
Brian Carlstrom83b1e682016-03-12 16:07:59 -0800156 deferredSaveThread->run("DeferredSaveThread");
Jamie Gennis99c3d702011-11-08 17:59:36 -0800157 }
Jamie Gennis76601082011-11-06 14:14:33 -0800158 }
159}
160
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800161EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
162 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800163 Mutex::Autolock lock(mMutex);
164
165 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000166 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800167 return 0;
168 }
169
170 if (mInitialized) {
171 sp<BlobCache> bc = getBlobCacheLocked();
172 return bc->get(key, keySize, value, valueSize);
173 }
174 return 0;
175}
176
Jamie Gennis98c63832011-11-07 17:03:54 -0800177void egl_cache_t::setCacheFilename(const char* filename) {
178 Mutex::Autolock lock(mMutex);
179 mFilename = filename;
180}
181
Jamie Gennis76601082011-11-06 14:14:33 -0800182sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
183 if (mBlobCache == NULL) {
184 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
185 loadBlobCacheLocked();
186 }
187 return mBlobCache;
188}
189
Jamie Gennis98c63832011-11-07 17:03:54 -0800190static uint32_t crc32c(const uint8_t* buf, size_t len) {
191 const uint32_t polyBits = 0x82F63B78;
192 uint32_t r = 0;
193 for (size_t i = 0; i < len; i++) {
194 r ^= buf[i];
195 for (int j = 0; j < 8; j++) {
196 if (r & 1) {
197 r = (r >> 1) ^ polyBits;
198 } else {
199 r >>= 1;
200 }
201 }
202 }
203 return r;
204}
205
Jamie Gennis76601082011-11-06 14:14:33 -0800206void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis5539e212013-07-30 15:10:02 -0700207 if (mFilename.length() > 0 && mBlobCache != NULL) {
Jamie Gennis98c63832011-11-07 17:03:54 -0800208 size_t cacheSize = mBlobCache->getFlattenedSize();
209 size_t headerSize = cacheFileHeaderSize;
210 const char* fname = mFilename.string();
211
212 // Try to create the file with no permissions so we can write it
213 // without anyone trying to read it.
214 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
215 if (fd == -1) {
216 if (errno == EEXIST) {
217 // The file exists, delete it and try again.
218 if (unlink(fname) == -1) {
219 // No point in retrying if the unlink failed.
Steve Blocke6f43dd2012-01-06 19:20:56 +0000220 ALOGE("error unlinking cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800221 strerror(errno), errno);
222 return;
223 }
224 // Retry now that we've unlinked the file.
225 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
226 }
227 if (fd == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000228 ALOGE("error creating cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800229 strerror(errno), errno);
230 return;
231 }
232 }
233
234 size_t fileSize = headerSize + cacheSize;
Jamie Gennis98c63832011-11-07 17:03:54 -0800235
vijay guptaa30cc7d2012-06-27 16:14:42 -0700236 uint8_t* buf = new uint8_t [fileSize];
237 if (!buf) {
238 ALOGE("error allocating buffer for cache contents: %s (%d)",
239 strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800240 close(fd);
241 unlink(fname);
242 return;
243 }
244
Mathias Agopiane1424282013-07-29 21:24:40 -0700245 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize);
Jamie Gennis98c63832011-11-07 17:03:54 -0800246 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000247 ALOGE("error writing cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800248 -err);
vijay guptaa30cc7d2012-06-27 16:14:42 -0700249 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800250 close(fd);
251 unlink(fname);
252 return;
253 }
254
255 // Write the file magic and CRC
256 memcpy(buf, cacheFileMagic, 4);
257 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
258 *crc = crc32c(buf + headerSize, cacheSize);
259
vijay guptaa30cc7d2012-06-27 16:14:42 -0700260 if (write(fd, buf, fileSize) == -1) {
261 ALOGE("error writing cache file: %s (%d)", strerror(errno),
262 errno);
263 delete [] buf;
264 close(fd);
265 unlink(fname);
266 return;
267 }
268
269 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800270 fchmod(fd, S_IRUSR);
271 close(fd);
272 }
Jamie Gennis76601082011-11-06 14:14:33 -0800273}
274
275void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800276 if (mFilename.length() > 0) {
277 size_t headerSize = cacheFileHeaderSize;
278
279 int fd = open(mFilename.string(), O_RDONLY, 0);
280 if (fd == -1) {
281 if (errno != ENOENT) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000282 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(),
Jamie Gennis98c63832011-11-07 17:03:54 -0800283 strerror(errno), errno);
284 }
285 return;
286 }
287
288 struct stat statBuf;
289 if (fstat(fd, &statBuf) == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000290 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800291 close(fd);
292 return;
293 }
294
295 // Sanity check the size before trying to mmap it.
296 size_t fileSize = statBuf.st_size;
297 if (fileSize > maxTotalSize * 2) {
Dan Alberteacd31f2016-02-02 15:08:34 -0800298 ALOGE("cache file is too large: %#" PRIx64,
299 static_cast<off64_t>(statBuf.st_size));
Jamie Gennis98c63832011-11-07 17:03:54 -0800300 close(fd);
301 return;
302 }
303
304 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
305 PROT_READ, MAP_PRIVATE, fd, 0));
306 if (buf == MAP_FAILED) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000307 ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
Jamie Gennis98c63832011-11-07 17:03:54 -0800308 errno);
309 close(fd);
310 return;
311 }
312
313 // Check the file magic and CRC
314 size_t cacheSize = fileSize - headerSize;
315 if (memcmp(buf, cacheFileMagic, 4) != 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000316 ALOGE("cache file has bad mojo");
Jamie Gennis98c63832011-11-07 17:03:54 -0800317 close(fd);
318 return;
319 }
320 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
321 if (crc32c(buf + headerSize, cacheSize) != *crc) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000322 ALOGE("cache file failed CRC check");
Jamie Gennis98c63832011-11-07 17:03:54 -0800323 close(fd);
324 return;
325 }
326
Mathias Agopiane1424282013-07-29 21:24:40 -0700327 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize);
Jamie Gennis98c63832011-11-07 17:03:54 -0800328 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000329 ALOGE("error reading cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800330 -err);
331 munmap(buf, fileSize);
332 close(fd);
333 return;
334 }
335
336 munmap(buf, fileSize);
337 close(fd);
338 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700339}
340
341// ----------------------------------------------------------------------------
342}; // namespace android
343// ----------------------------------------------------------------------------