blob: 03397a998687d6ff9516eeb830aed51af5627bd7 [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>
24#include <sys/mman.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <unistd.h>
28
Jamie Gennis89c1d612011-11-19 16:25:24 -080029#ifndef MAX_EGL_CACHE_ENTRY_SIZE
30#define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024);
31#endif
32
Jamie Gennisf478e6d2012-09-12 11:55:16 -070033#ifndef MAX_EGL_CACHE_KEY_SIZE
34#define MAX_EGL_CACHE_KEY_SIZE (1024);
35#endif
36
Jamie Gennis89c1d612011-11-19 16:25:24 -080037#ifndef MAX_EGL_CACHE_SIZE
38#define MAX_EGL_CACHE_SIZE (64 * 1024);
39#endif
40
Jamie Gennis76601082011-11-06 14:14:33 -080041// Cache size limits.
Jamie Gennisf478e6d2012-09-12 11:55:16 -070042static const size_t maxKeySize = MAX_EGL_CACHE_KEY_SIZE;
Jamie Gennis89c1d612011-11-19 16:25:24 -080043static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE;
44static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE;
Jamie Gennis76601082011-11-06 14:14:33 -080045
Jamie Gennis98c63832011-11-07 17:03:54 -080046// Cache file header
47static const char* cacheFileMagic = "EGL$";
48static const size_t cacheFileHeaderSize = 8;
49
Jamie Gennis99c3d702011-11-08 17:59:36 -080050// The time in seconds to wait before saving newly inserted cache entries.
51static const unsigned int deferredSaveDelay = 4;
52
Jamie Gennisaca51c02011-11-03 17:42:43 -070053// ----------------------------------------------------------------------------
54namespace android {
55// ----------------------------------------------------------------------------
56
57#define BC_EXT_STR "EGL_ANDROID_blob_cache"
58
59//
Jamie Gennis76601082011-11-06 14:14:33 -080060// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070061//
Jamie Gennisc42fcf02011-11-09 15:35:34 -080062static void setBlob(const void* key, EGLsizeiANDROID keySize,
63 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080064 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070065}
66
Jamie Gennisc42fcf02011-11-09 15:35:34 -080067static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
68 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080069 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
70}
71
72//
73// egl_cache_t definition
74//
75egl_cache_t::egl_cache_t() :
76 mInitialized(false),
77 mBlobCache(NULL) {
78}
79
80egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070081}
82
Jamie Gennis98c63832011-11-07 17:03:54 -080083egl_cache_t egl_cache_t::sCache;
84
Jamie Gennisaca51c02011-11-03 17:42:43 -070085egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080086 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070087}
88
89void egl_cache_t::initialize(egl_display_t *display) {
Jamie Gennis76601082011-11-06 14:14:33 -080090 Mutex::Autolock lock(mMutex);
Mathias Agopianada798b2012-02-13 17:09:30 -080091
92 egl_connection_t* const cnx = &gEGLImpl;
93 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
94 const char* exts = display->disp.queryString.extensions;
95 size_t bcExtLen = strlen(BC_EXT_STR);
96 size_t extsLen = strlen(exts);
97 bool equal = !strcmp(BC_EXT_STR, exts);
98 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
99 bool atEnd = (bcExtLen+1) < extsLen &&
100 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
101 bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
102 if (equal || atStart || atEnd || inMiddle) {
103 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
104 eglSetBlobCacheFuncsANDROID =
105 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800106 cnx->egl.eglGetProcAddress(
107 "eglSetBlobCacheFuncsANDROID"));
Mathias Agopianada798b2012-02-13 17:09:30 -0800108 if (eglSetBlobCacheFuncsANDROID == NULL) {
109 ALOGE("EGL_ANDROID_blob_cache advertised, "
110 "but unable to get eglSetBlobCacheFuncsANDROID");
111 return;
112 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700113
Mathias Agopianada798b2012-02-13 17:09:30 -0800114 eglSetBlobCacheFuncsANDROID(display->disp.dpy,
115 android::setBlob, android::getBlob);
116 EGLint err = cnx->egl.eglGetError();
117 if (err != EGL_SUCCESS) {
118 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
119 "%#x", err);
Jamie Gennisaca51c02011-11-03 17:42:43 -0700120 }
121 }
122 }
Mathias Agopianada798b2012-02-13 17:09:30 -0800123
Jamie Gennis76601082011-11-06 14:14:33 -0800124 mInitialized = true;
125}
126
127void egl_cache_t::terminate() {
128 Mutex::Autolock lock(mMutex);
129 if (mBlobCache != NULL) {
130 saveBlobCacheLocked();
131 mBlobCache = NULL;
132 }
133 mInitialized = false;
134}
135
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800136void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
137 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800138 Mutex::Autolock lock(mMutex);
139
140 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000141 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800142 return;
143 }
144
145 if (mInitialized) {
146 sp<BlobCache> bc = getBlobCacheLocked();
147 bc->set(key, keySize, value, valueSize);
Jamie Gennis99c3d702011-11-08 17:59:36 -0800148
149 if (!mSavePending) {
150 class DeferredSaveThread : public Thread {
151 public:
152 DeferredSaveThread() : Thread(false) {}
153
154 virtual bool threadLoop() {
155 sleep(deferredSaveDelay);
156 egl_cache_t* c = egl_cache_t::get();
157 Mutex::Autolock lock(c->mMutex);
158 if (c->mInitialized) {
159 c->saveBlobCacheLocked();
160 }
161 c->mSavePending = false;
162 return false;
163 }
164 };
165
166 // The thread will hold a strong ref to itself until it has finished
167 // running, so there's no need to keep a ref around.
168 sp<Thread> deferredSaveThread(new DeferredSaveThread());
169 mSavePending = true;
170 deferredSaveThread->run();
171 }
Jamie Gennis76601082011-11-06 14:14:33 -0800172 }
173}
174
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800175EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
176 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800177 Mutex::Autolock lock(mMutex);
178
179 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000180 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800181 return 0;
182 }
183
184 if (mInitialized) {
185 sp<BlobCache> bc = getBlobCacheLocked();
186 return bc->get(key, keySize, value, valueSize);
187 }
188 return 0;
189}
190
Jamie Gennis98c63832011-11-07 17:03:54 -0800191void egl_cache_t::setCacheFilename(const char* filename) {
192 Mutex::Autolock lock(mMutex);
193 mFilename = filename;
194}
195
Jamie Gennis76601082011-11-06 14:14:33 -0800196sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
197 if (mBlobCache == NULL) {
198 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
199 loadBlobCacheLocked();
200 }
201 return mBlobCache;
202}
203
Jamie Gennis98c63832011-11-07 17:03:54 -0800204static uint32_t crc32c(const uint8_t* buf, size_t len) {
205 const uint32_t polyBits = 0x82F63B78;
206 uint32_t r = 0;
207 for (size_t i = 0; i < len; i++) {
208 r ^= buf[i];
209 for (int j = 0; j < 8; j++) {
210 if (r & 1) {
211 r = (r >> 1) ^ polyBits;
212 } else {
213 r >>= 1;
214 }
215 }
216 }
217 return r;
218}
219
Jamie Gennis76601082011-11-06 14:14:33 -0800220void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800221 if (mFilename.length() > 0) {
222 size_t cacheSize = mBlobCache->getFlattenedSize();
223 size_t headerSize = cacheFileHeaderSize;
224 const char* fname = mFilename.string();
225
226 // Try to create the file with no permissions so we can write it
227 // without anyone trying to read it.
228 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
229 if (fd == -1) {
230 if (errno == EEXIST) {
231 // The file exists, delete it and try again.
232 if (unlink(fname) == -1) {
233 // No point in retrying if the unlink failed.
Steve Blocke6f43dd2012-01-06 19:20:56 +0000234 ALOGE("error unlinking cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800235 strerror(errno), errno);
236 return;
237 }
238 // Retry now that we've unlinked the file.
239 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
240 }
241 if (fd == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000242 ALOGE("error creating cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800243 strerror(errno), errno);
244 return;
245 }
246 }
247
248 size_t fileSize = headerSize + cacheSize;
Jamie Gennis98c63832011-11-07 17:03:54 -0800249
vijay guptaa30cc7d2012-06-27 16:14:42 -0700250 uint8_t* buf = new uint8_t [fileSize];
251 if (!buf) {
252 ALOGE("error allocating buffer for cache contents: %s (%d)",
253 strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800254 close(fd);
255 unlink(fname);
256 return;
257 }
258
259 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL,
260 0);
261 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000262 ALOGE("error writing cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800263 -err);
vijay guptaa30cc7d2012-06-27 16:14:42 -0700264 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800265 close(fd);
266 unlink(fname);
267 return;
268 }
269
270 // Write the file magic and CRC
271 memcpy(buf, cacheFileMagic, 4);
272 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
273 *crc = crc32c(buf + headerSize, cacheSize);
274
vijay guptaa30cc7d2012-06-27 16:14:42 -0700275 if (write(fd, buf, fileSize) == -1) {
276 ALOGE("error writing cache file: %s (%d)", strerror(errno),
277 errno);
278 delete [] buf;
279 close(fd);
280 unlink(fname);
281 return;
282 }
283
284 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800285 fchmod(fd, S_IRUSR);
286 close(fd);
287 }
Jamie Gennis76601082011-11-06 14:14:33 -0800288}
289
290void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800291 if (mFilename.length() > 0) {
292 size_t headerSize = cacheFileHeaderSize;
293
294 int fd = open(mFilename.string(), O_RDONLY, 0);
295 if (fd == -1) {
296 if (errno != ENOENT) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000297 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(),
Jamie Gennis98c63832011-11-07 17:03:54 -0800298 strerror(errno), errno);
299 }
300 return;
301 }
302
303 struct stat statBuf;
304 if (fstat(fd, &statBuf) == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000305 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800306 close(fd);
307 return;
308 }
309
310 // Sanity check the size before trying to mmap it.
311 size_t fileSize = statBuf.st_size;
312 if (fileSize > maxTotalSize * 2) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000313 ALOGE("cache file is too large: %#llx", statBuf.st_size);
Jamie Gennis98c63832011-11-07 17:03:54 -0800314 close(fd);
315 return;
316 }
317
318 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
319 PROT_READ, MAP_PRIVATE, fd, 0));
320 if (buf == MAP_FAILED) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000321 ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
Jamie Gennis98c63832011-11-07 17:03:54 -0800322 errno);
323 close(fd);
324 return;
325 }
326
327 // Check the file magic and CRC
328 size_t cacheSize = fileSize - headerSize;
329 if (memcmp(buf, cacheFileMagic, 4) != 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000330 ALOGE("cache file has bad mojo");
Jamie Gennis98c63832011-11-07 17:03:54 -0800331 close(fd);
332 return;
333 }
334 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
335 if (crc32c(buf + headerSize, cacheSize) != *crc) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000336 ALOGE("cache file failed CRC check");
Jamie Gennis98c63832011-11-07 17:03:54 -0800337 close(fd);
338 return;
339 }
340
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800341 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL,
342 0);
Jamie Gennis98c63832011-11-07 17:03:54 -0800343 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000344 ALOGE("error reading cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800345 -err);
346 munmap(buf, fileSize);
347 close(fd);
348 return;
349 }
350
351 munmap(buf, fileSize);
352 close(fd);
353 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700354}
355
356// ----------------------------------------------------------------------------
357}; // namespace android
358// ----------------------------------------------------------------------------