blob: f368d753d7f90d34430191df789aeca00b1d6a46 [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 Gennis89c1d612011-11-19 16:25:24 -080030#ifndef MAX_EGL_CACHE_ENTRY_SIZE
31#define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024);
32#endif
33
Jamie Gennisf478e6d2012-09-12 11:55:16 -070034#ifndef MAX_EGL_CACHE_KEY_SIZE
35#define MAX_EGL_CACHE_KEY_SIZE (1024);
36#endif
37
Jamie Gennis89c1d612011-11-19 16:25:24 -080038#ifndef MAX_EGL_CACHE_SIZE
39#define MAX_EGL_CACHE_SIZE (64 * 1024);
40#endif
41
Jamie Gennis76601082011-11-06 14:14:33 -080042// Cache size limits.
Jamie Gennisf478e6d2012-09-12 11:55:16 -070043static const size_t maxKeySize = MAX_EGL_CACHE_KEY_SIZE;
Jamie Gennis89c1d612011-11-19 16:25:24 -080044static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE;
45static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE;
Jamie Gennis76601082011-11-06 14:14:33 -080046
Jamie Gennis98c63832011-11-07 17:03:54 -080047// Cache file header
48static const char* cacheFileMagic = "EGL$";
49static const size_t cacheFileHeaderSize = 8;
50
Jamie Gennis99c3d702011-11-08 17:59:36 -080051// The time in seconds to wait before saving newly inserted cache entries.
52static const unsigned int deferredSaveDelay = 4;
53
Jamie Gennisaca51c02011-11-03 17:42:43 -070054// ----------------------------------------------------------------------------
55namespace android {
56// ----------------------------------------------------------------------------
57
58#define BC_EXT_STR "EGL_ANDROID_blob_cache"
59
60//
Jamie Gennis76601082011-11-06 14:14:33 -080061// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070062//
Jamie Gennisc42fcf02011-11-09 15:35:34 -080063static void setBlob(const void* key, EGLsizeiANDROID keySize,
64 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080065 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070066}
67
Jamie Gennisc42fcf02011-11-09 15:35:34 -080068static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
69 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080070 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
71}
72
73//
74// egl_cache_t definition
75//
76egl_cache_t::egl_cache_t() :
77 mInitialized(false),
78 mBlobCache(NULL) {
79}
80
81egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070082}
83
Jamie Gennis98c63832011-11-07 17:03:54 -080084egl_cache_t egl_cache_t::sCache;
85
Jamie Gennisaca51c02011-11-03 17:42:43 -070086egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080087 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070088}
89
90void egl_cache_t::initialize(egl_display_t *display) {
Jamie Gennis76601082011-11-06 14:14:33 -080091 Mutex::Autolock lock(mMutex);
Mathias Agopianada798b2012-02-13 17:09:30 -080092
93 egl_connection_t* const cnx = &gEGLImpl;
94 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
95 const char* exts = display->disp.queryString.extensions;
96 size_t bcExtLen = strlen(BC_EXT_STR);
97 size_t extsLen = strlen(exts);
98 bool equal = !strcmp(BC_EXT_STR, exts);
99 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
100 bool atEnd = (bcExtLen+1) < extsLen &&
101 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
102 bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
103 if (equal || atStart || atEnd || inMiddle) {
104 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
105 eglSetBlobCacheFuncsANDROID =
106 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800107 cnx->egl.eglGetProcAddress(
108 "eglSetBlobCacheFuncsANDROID"));
Mathias Agopianada798b2012-02-13 17:09:30 -0800109 if (eglSetBlobCacheFuncsANDROID == NULL) {
110 ALOGE("EGL_ANDROID_blob_cache advertised, "
111 "but unable to get eglSetBlobCacheFuncsANDROID");
112 return;
113 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700114
Mathias Agopianada798b2012-02-13 17:09:30 -0800115 eglSetBlobCacheFuncsANDROID(display->disp.dpy,
116 android::setBlob, android::getBlob);
117 EGLint err = cnx->egl.eglGetError();
118 if (err != EGL_SUCCESS) {
119 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
120 "%#x", err);
Jamie Gennisaca51c02011-11-03 17:42:43 -0700121 }
122 }
123 }
Mathias Agopianada798b2012-02-13 17:09:30 -0800124
Jamie Gennis76601082011-11-06 14:14:33 -0800125 mInitialized = true;
126}
127
128void egl_cache_t::terminate() {
129 Mutex::Autolock lock(mMutex);
Jamie Gennis5539e212013-07-30 15:10:02 -0700130 saveBlobCacheLocked();
131 mBlobCache = NULL;
Jamie Gennis76601082011-11-06 14:14:33 -0800132}
133
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800134void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
135 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800136 Mutex::Autolock lock(mMutex);
137
138 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000139 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800140 return;
141 }
142
143 if (mInitialized) {
144 sp<BlobCache> bc = getBlobCacheLocked();
145 bc->set(key, keySize, value, valueSize);
Jamie Gennis99c3d702011-11-08 17:59:36 -0800146
147 if (!mSavePending) {
148 class DeferredSaveThread : public Thread {
149 public:
150 DeferredSaveThread() : Thread(false) {}
151
152 virtual bool threadLoop() {
153 sleep(deferredSaveDelay);
154 egl_cache_t* c = egl_cache_t::get();
155 Mutex::Autolock lock(c->mMutex);
156 if (c->mInitialized) {
157 c->saveBlobCacheLocked();
158 }
159 c->mSavePending = false;
160 return false;
161 }
162 };
163
164 // The thread will hold a strong ref to itself until it has finished
165 // running, so there's no need to keep a ref around.
166 sp<Thread> deferredSaveThread(new DeferredSaveThread());
167 mSavePending = true;
168 deferredSaveThread->run();
169 }
Jamie Gennis76601082011-11-06 14:14:33 -0800170 }
171}
172
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800173EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
174 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -0800175 Mutex::Autolock lock(mMutex);
176
177 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000178 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800179 return 0;
180 }
181
182 if (mInitialized) {
183 sp<BlobCache> bc = getBlobCacheLocked();
184 return bc->get(key, keySize, value, valueSize);
185 }
186 return 0;
187}
188
Jamie Gennis98c63832011-11-07 17:03:54 -0800189void egl_cache_t::setCacheFilename(const char* filename) {
190 Mutex::Autolock lock(mMutex);
191 mFilename = filename;
192}
193
Jamie Gennis76601082011-11-06 14:14:33 -0800194sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
195 if (mBlobCache == NULL) {
196 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
197 loadBlobCacheLocked();
198 }
199 return mBlobCache;
200}
201
Jamie Gennis98c63832011-11-07 17:03:54 -0800202static uint32_t crc32c(const uint8_t* buf, size_t len) {
203 const uint32_t polyBits = 0x82F63B78;
204 uint32_t r = 0;
205 for (size_t i = 0; i < len; i++) {
206 r ^= buf[i];
207 for (int j = 0; j < 8; j++) {
208 if (r & 1) {
209 r = (r >> 1) ^ polyBits;
210 } else {
211 r >>= 1;
212 }
213 }
214 }
215 return r;
216}
217
Jamie Gennis76601082011-11-06 14:14:33 -0800218void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis5539e212013-07-30 15:10:02 -0700219 if (mFilename.length() > 0 && mBlobCache != NULL) {
Jamie Gennis98c63832011-11-07 17:03:54 -0800220 size_t cacheSize = mBlobCache->getFlattenedSize();
221 size_t headerSize = cacheFileHeaderSize;
222 const char* fname = mFilename.string();
223
224 // Try to create the file with no permissions so we can write it
225 // without anyone trying to read it.
226 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
227 if (fd == -1) {
228 if (errno == EEXIST) {
229 // The file exists, delete it and try again.
230 if (unlink(fname) == -1) {
231 // No point in retrying if the unlink failed.
Steve Blocke6f43dd2012-01-06 19:20:56 +0000232 ALOGE("error unlinking cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800233 strerror(errno), errno);
234 return;
235 }
236 // Retry now that we've unlinked the file.
237 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
238 }
239 if (fd == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000240 ALOGE("error creating cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800241 strerror(errno), errno);
242 return;
243 }
244 }
245
246 size_t fileSize = headerSize + cacheSize;
Jamie Gennis98c63832011-11-07 17:03:54 -0800247
vijay guptaa30cc7d2012-06-27 16:14:42 -0700248 uint8_t* buf = new uint8_t [fileSize];
249 if (!buf) {
250 ALOGE("error allocating buffer for cache contents: %s (%d)",
251 strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800252 close(fd);
253 unlink(fname);
254 return;
255 }
256
Mathias Agopiane1424282013-07-29 21:24:40 -0700257 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize);
Jamie Gennis98c63832011-11-07 17:03:54 -0800258 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000259 ALOGE("error writing cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800260 -err);
vijay guptaa30cc7d2012-06-27 16:14:42 -0700261 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800262 close(fd);
263 unlink(fname);
264 return;
265 }
266
267 // Write the file magic and CRC
268 memcpy(buf, cacheFileMagic, 4);
269 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
270 *crc = crc32c(buf + headerSize, cacheSize);
271
vijay guptaa30cc7d2012-06-27 16:14:42 -0700272 if (write(fd, buf, fileSize) == -1) {
273 ALOGE("error writing cache file: %s (%d)", strerror(errno),
274 errno);
275 delete [] buf;
276 close(fd);
277 unlink(fname);
278 return;
279 }
280
281 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800282 fchmod(fd, S_IRUSR);
283 close(fd);
284 }
Jamie Gennis76601082011-11-06 14:14:33 -0800285}
286
287void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800288 if (mFilename.length() > 0) {
289 size_t headerSize = cacheFileHeaderSize;
290
291 int fd = open(mFilename.string(), O_RDONLY, 0);
292 if (fd == -1) {
293 if (errno != ENOENT) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000294 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(),
Jamie Gennis98c63832011-11-07 17:03:54 -0800295 strerror(errno), errno);
296 }
297 return;
298 }
299
300 struct stat statBuf;
301 if (fstat(fd, &statBuf) == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000302 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800303 close(fd);
304 return;
305 }
306
307 // Sanity check the size before trying to mmap it.
308 size_t fileSize = statBuf.st_size;
309 if (fileSize > maxTotalSize * 2) {
Dan Alberteacd31f2016-02-02 15:08:34 -0800310 ALOGE("cache file is too large: %#" PRIx64,
311 static_cast<off64_t>(statBuf.st_size));
Jamie Gennis98c63832011-11-07 17:03:54 -0800312 close(fd);
313 return;
314 }
315
316 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
317 PROT_READ, MAP_PRIVATE, fd, 0));
318 if (buf == MAP_FAILED) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000319 ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
Jamie Gennis98c63832011-11-07 17:03:54 -0800320 errno);
321 close(fd);
322 return;
323 }
324
325 // Check the file magic and CRC
326 size_t cacheSize = fileSize - headerSize;
327 if (memcmp(buf, cacheFileMagic, 4) != 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000328 ALOGE("cache file has bad mojo");
Jamie Gennis98c63832011-11-07 17:03:54 -0800329 close(fd);
330 return;
331 }
332 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
333 if (crc32c(buf + headerSize, cacheSize) != *crc) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000334 ALOGE("cache file failed CRC check");
Jamie Gennis98c63832011-11-07 17:03:54 -0800335 close(fd);
336 return;
337 }
338
Mathias Agopiane1424282013-07-29 21:24:40 -0700339 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize);
Jamie Gennis98c63832011-11-07 17:03:54 -0800340 if (err != OK) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000341 ALOGE("error reading cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800342 -err);
343 munmap(buf, fileSize);
344 close(fd);
345 return;
346 }
347
348 munmap(buf, fileSize);
349 close(fd);
350 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700351}
352
353// ----------------------------------------------------------------------------
354}; // namespace android
355// ----------------------------------------------------------------------------