blob: aa40d5888ce58a68d9ba6bbdd45adf43ae3b9853 [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
17#include "egl_cache.h"
18#include "egl_display.h"
19#include "egl_impl.h"
20#include "egldefs.h"
21
Jamie Gennis98c63832011-11-07 17:03:54 -080022#include <fcntl.h>
23#include <sys/mman.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <unistd.h>
27
Jamie Gennis76601082011-11-06 14:14:33 -080028// Cache size limits.
29static const size_t maxKeySize = 1024;
30static const size_t maxValueSize = 4096;
31static const size_t maxTotalSize = 64 * 1024;
32
Jamie Gennis98c63832011-11-07 17:03:54 -080033// Cache file header
34static const char* cacheFileMagic = "EGL$";
35static const size_t cacheFileHeaderSize = 8;
36
Jamie Gennisaca51c02011-11-03 17:42:43 -070037// ----------------------------------------------------------------------------
38namespace android {
39// ----------------------------------------------------------------------------
40
41#define BC_EXT_STR "EGL_ANDROID_blob_cache"
42
43//
Jamie Gennis76601082011-11-06 14:14:33 -080044// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070045//
46static void setBlob(const void* key, EGLsizei keySize, const void* value,
47 EGLsizei valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080048 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070049}
50
51static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value,
52 EGLsizei valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080053 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
54}
55
56//
57// egl_cache_t definition
58//
59egl_cache_t::egl_cache_t() :
60 mInitialized(false),
61 mBlobCache(NULL) {
62}
63
64egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070065}
66
Jamie Gennis98c63832011-11-07 17:03:54 -080067egl_cache_t egl_cache_t::sCache;
68
Jamie Gennisaca51c02011-11-03 17:42:43 -070069egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080070 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070071}
72
73void egl_cache_t::initialize(egl_display_t *display) {
Jamie Gennis76601082011-11-06 14:14:33 -080074 Mutex::Autolock lock(mMutex);
Jamie Gennisaca51c02011-11-03 17:42:43 -070075 for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
76 egl_connection_t* const cnx = &gEGLImpl[i];
77 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
78 const char* exts = display->disp[i].queryString.extensions;
79 size_t bcExtLen = strlen(BC_EXT_STR);
80 size_t extsLen = strlen(exts);
81 bool equal = !strcmp(BC_EXT_STR, exts);
82 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
83 bool atEnd = (bcExtLen+1) < extsLen &&
84 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
85 bool inMiddle = strstr(" " BC_EXT_STR " ", exts);
86 if (equal || atStart || atEnd || inMiddle) {
87 PFNEGLSETBLOBCACHEFUNCSPROC eglSetBlobCacheFuncs;
88 eglSetBlobCacheFuncs =
89 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSPROC>(
90 cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncs"));
91 if (eglSetBlobCacheFuncs == NULL) {
92 LOGE("EGL_ANDROID_blob_cache advertised by display %d, "
93 "but unable to get eglSetBlobCacheFuncs", i);
94 continue;
95 }
96
Jamie Gennis76601082011-11-06 14:14:33 -080097 eglSetBlobCacheFuncs(display->disp[i].dpy, android::setBlob,
98 android::getBlob);
Jamie Gennisaca51c02011-11-03 17:42:43 -070099 EGLint err = cnx->egl.eglGetError();
100 if (err != EGL_SUCCESS) {
101 LOGE("eglSetBlobCacheFuncs resulted in an error: %#x",
102 err);
103 }
104 }
105 }
106 }
Jamie Gennis76601082011-11-06 14:14:33 -0800107 mInitialized = true;
108}
109
110void egl_cache_t::terminate() {
111 Mutex::Autolock lock(mMutex);
112 if (mBlobCache != NULL) {
113 saveBlobCacheLocked();
114 mBlobCache = NULL;
115 }
116 mInitialized = false;
117}
118
119void egl_cache_t::setBlob(const void* key, EGLsizei keySize, const void* value,
120 EGLsizei valueSize) {
121 Mutex::Autolock lock(mMutex);
122
123 if (keySize < 0 || valueSize < 0) {
124 LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
125 return;
126 }
127
128 if (mInitialized) {
129 sp<BlobCache> bc = getBlobCacheLocked();
130 bc->set(key, keySize, value, valueSize);
131 }
132}
133
134EGLsizei egl_cache_t::getBlob(const void* key, EGLsizei keySize, void* value,
135 EGLsizei valueSize) {
136 Mutex::Autolock lock(mMutex);
137
138 if (keySize < 0 || valueSize < 0) {
139 LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
140 return 0;
141 }
142
143 if (mInitialized) {
144 sp<BlobCache> bc = getBlobCacheLocked();
145 return bc->get(key, keySize, value, valueSize);
146 }
147 return 0;
148}
149
Jamie Gennis98c63832011-11-07 17:03:54 -0800150void egl_cache_t::setCacheFilename(const char* filename) {
151 Mutex::Autolock lock(mMutex);
152 mFilename = filename;
153}
154
Jamie Gennis76601082011-11-06 14:14:33 -0800155sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
156 if (mBlobCache == NULL) {
157 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
158 loadBlobCacheLocked();
159 }
160 return mBlobCache;
161}
162
Jamie Gennis98c63832011-11-07 17:03:54 -0800163static uint32_t crc32c(const uint8_t* buf, size_t len) {
164 const uint32_t polyBits = 0x82F63B78;
165 uint32_t r = 0;
166 for (size_t i = 0; i < len; i++) {
167 r ^= buf[i];
168 for (int j = 0; j < 8; j++) {
169 if (r & 1) {
170 r = (r >> 1) ^ polyBits;
171 } else {
172 r >>= 1;
173 }
174 }
175 }
176 return r;
177}
178
Jamie Gennis76601082011-11-06 14:14:33 -0800179void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800180 if (mFilename.length() > 0) {
181 size_t cacheSize = mBlobCache->getFlattenedSize();
182 size_t headerSize = cacheFileHeaderSize;
183 const char* fname = mFilename.string();
184
185 // Try to create the file with no permissions so we can write it
186 // without anyone trying to read it.
187 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
188 if (fd == -1) {
189 if (errno == EEXIST) {
190 // The file exists, delete it and try again.
191 if (unlink(fname) == -1) {
192 // No point in retrying if the unlink failed.
193 LOGE("error unlinking cache file %s: %s (%d)", fname,
194 strerror(errno), errno);
195 return;
196 }
197 // Retry now that we've unlinked the file.
198 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
199 }
200 if (fd == -1) {
201 LOGE("error creating cache file %s: %s (%d)", fname,
202 strerror(errno), errno);
203 return;
204 }
205 }
206
207 size_t fileSize = headerSize + cacheSize;
208 if (ftruncate(fd, fileSize) == -1) {
209 LOGE("error setting cache file size: %s (%d)", strerror(errno),
210 errno);
211 close(fd);
212 unlink(fname);
213 return;
214 }
215
216 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
217 PROT_WRITE, MAP_SHARED, fd, 0));
218 if (buf == MAP_FAILED) {
219 LOGE("error mmaping cache file: %s (%d)", strerror(errno),
220 errno);
221 close(fd);
222 unlink(fname);
223 return;
224 }
225
226 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL,
227 0);
228 if (err != OK) {
229 LOGE("error writing cache contents: %s (%d)", strerror(-err),
230 -err);
231 munmap(buf, fileSize);
232 close(fd);
233 unlink(fname);
234 return;
235 }
236
237 // Write the file magic and CRC
238 memcpy(buf, cacheFileMagic, 4);
239 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
240 *crc = crc32c(buf + headerSize, cacheSize);
241
242 munmap(buf, fileSize);
243 fchmod(fd, S_IRUSR);
244 close(fd);
245 }
Jamie Gennis76601082011-11-06 14:14:33 -0800246}
247
248void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800249 if (mFilename.length() > 0) {
250 size_t headerSize = cacheFileHeaderSize;
251
252 int fd = open(mFilename.string(), O_RDONLY, 0);
253 if (fd == -1) {
254 if (errno != ENOENT) {
255 LOGE("error opening cache file %s: %s (%d)", mFilename.string(),
256 strerror(errno), errno);
257 }
258 return;
259 }
260
261 struct stat statBuf;
262 if (fstat(fd, &statBuf) == -1) {
263 LOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
264 close(fd);
265 return;
266 }
267
268 // Sanity check the size before trying to mmap it.
269 size_t fileSize = statBuf.st_size;
270 if (fileSize > maxTotalSize * 2) {
271 LOGE("cache file is too large: %#llx", statBuf.st_size);
272 close(fd);
273 return;
274 }
275
276 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
277 PROT_READ, MAP_PRIVATE, fd, 0));
278 if (buf == MAP_FAILED) {
279 LOGE("error mmaping cache file: %s (%d)", strerror(errno),
280 errno);
281 close(fd);
282 return;
283 }
284
285 // Check the file magic and CRC
286 size_t cacheSize = fileSize - headerSize;
287 if (memcmp(buf, cacheFileMagic, 4) != 0) {
288 LOGE("cache file has bad mojo");
289 close(fd);
290 return;
291 }
292 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
293 if (crc32c(buf + headerSize, cacheSize) != *crc) {
294 LOGE("cache file failed CRC check");
295 close(fd);
296 return;
297 }
298
299 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL, 0);
300 if (err != OK) {
301 LOGE("error reading cache contents: %s (%d)", strerror(-err),
302 -err);
303 munmap(buf, fileSize);
304 close(fd);
305 return;
306 }
307
308 munmap(buf, fileSize);
309 close(fd);
310 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700311}
312
313// ----------------------------------------------------------------------------
314}; // namespace android
315// ----------------------------------------------------------------------------