blob: 0e7cee8e52fe2f4b6091f8440d037aec703e775f [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
Mathias Agopian65421432017-03-08 11:49:05 -080027#include <thread>
28
29#include <log/log.h>
Jamie Gennis98c63832011-11-07 17:03:54 -080030
Jamie Gennis76601082011-11-06 14:14:33 -080031// Cache size limits.
Dan Willemsenbace39e2016-10-11 15:42:59 -070032static const size_t maxKeySize = 12 * 1024;
33static const size_t maxValueSize = 64 * 1024;
34static const size_t maxTotalSize = 2 * 1024 * 1024;
Jamie Gennis76601082011-11-06 14:14:33 -080035
Jamie Gennis98c63832011-11-07 17:03:54 -080036// Cache file header
37static const char* cacheFileMagic = "EGL$";
38static const size_t cacheFileHeaderSize = 8;
39
Jamie Gennis99c3d702011-11-08 17:59:36 -080040// The time in seconds to wait before saving newly inserted cache entries.
41static const unsigned int deferredSaveDelay = 4;
42
Jamie Gennisaca51c02011-11-03 17:42:43 -070043// ----------------------------------------------------------------------------
44namespace android {
45// ----------------------------------------------------------------------------
46
47#define BC_EXT_STR "EGL_ANDROID_blob_cache"
48
49//
Jamie Gennis76601082011-11-06 14:14:33 -080050// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070051//
Jamie Gennisc42fcf02011-11-09 15:35:34 -080052static void setBlob(const void* key, EGLsizeiANDROID keySize,
53 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080054 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070055}
56
Jamie Gennisc42fcf02011-11-09 15:35:34 -080057static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
58 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080059 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
60}
61
62//
63// egl_cache_t definition
64//
65egl_cache_t::egl_cache_t() :
66 mInitialized(false),
67 mBlobCache(NULL) {
68}
69
70egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070071}
72
Jamie Gennis98c63832011-11-07 17:03:54 -080073egl_cache_t egl_cache_t::sCache;
74
Jamie Gennisaca51c02011-11-03 17:42:43 -070075egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080076 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070077}
78
79void egl_cache_t::initialize(egl_display_t *display) {
Mathias Agopian65421432017-03-08 11:49:05 -080080 std::lock_guard<std::mutex> lock(mMutex);
Mathias Agopianada798b2012-02-13 17:09:30 -080081
82 egl_connection_t* const cnx = &gEGLImpl;
83 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
84 const char* exts = display->disp.queryString.extensions;
85 size_t bcExtLen = strlen(BC_EXT_STR);
86 size_t extsLen = strlen(exts);
87 bool equal = !strcmp(BC_EXT_STR, exts);
88 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
89 bool atEnd = (bcExtLen+1) < extsLen &&
90 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
Mathias Agopian311b4792017-02-28 15:00:49 -080091 bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
Mathias Agopianada798b2012-02-13 17:09:30 -080092 if (equal || atStart || atEnd || inMiddle) {
93 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
94 eglSetBlobCacheFuncsANDROID =
95 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
Jamie Gennisc42fcf02011-11-09 15:35:34 -080096 cnx->egl.eglGetProcAddress(
97 "eglSetBlobCacheFuncsANDROID"));
Mathias Agopianada798b2012-02-13 17:09:30 -080098 if (eglSetBlobCacheFuncsANDROID == NULL) {
99 ALOGE("EGL_ANDROID_blob_cache advertised, "
100 "but unable to get eglSetBlobCacheFuncsANDROID");
101 return;
102 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700103
Mathias Agopianada798b2012-02-13 17:09:30 -0800104 eglSetBlobCacheFuncsANDROID(display->disp.dpy,
105 android::setBlob, android::getBlob);
106 EGLint err = cnx->egl.eglGetError();
107 if (err != EGL_SUCCESS) {
108 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
109 "%#x", err);
Jamie Gennisaca51c02011-11-03 17:42:43 -0700110 }
111 }
112 }
Mathias Agopianada798b2012-02-13 17:09:30 -0800113
Jamie Gennis76601082011-11-06 14:14:33 -0800114 mInitialized = true;
115}
116
117void egl_cache_t::terminate() {
Mathias Agopian65421432017-03-08 11:49:05 -0800118 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis5539e212013-07-30 15:10:02 -0700119 saveBlobCacheLocked();
120 mBlobCache = NULL;
Jamie Gennis76601082011-11-06 14:14:33 -0800121}
122
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800123void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
124 const void* value, EGLsizeiANDROID valueSize) {
Mathias Agopian65421432017-03-08 11:49:05 -0800125 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis76601082011-11-06 14:14:33 -0800126
127 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000128 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800129 return;
130 }
131
132 if (mInitialized) {
133 sp<BlobCache> bc = getBlobCacheLocked();
134 bc->set(key, keySize, value, valueSize);
Jamie Gennis99c3d702011-11-08 17:59:36 -0800135
136 if (!mSavePending) {
Jamie Gennis99c3d702011-11-08 17:59:36 -0800137 mSavePending = true;
Mathias Agopian65421432017-03-08 11:49:05 -0800138 std::thread deferredSaveThread([this]() {
139 sleep(deferredSaveDelay);
140 std::lock_guard<std::mutex> lock(mMutex);
141 if (mInitialized) {
142 saveBlobCacheLocked();
143 }
144 mSavePending = false;
145 });
146 deferredSaveThread.detach();
Jamie Gennis99c3d702011-11-08 17:59:36 -0800147 }
Jamie Gennis76601082011-11-06 14:14:33 -0800148 }
149}
150
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800151EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
152 void* value, EGLsizeiANDROID valueSize) {
Mathias Agopian65421432017-03-08 11:49:05 -0800153 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis76601082011-11-06 14:14:33 -0800154
155 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000156 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800157 return 0;
158 }
159
160 if (mInitialized) {
161 sp<BlobCache> bc = getBlobCacheLocked();
162 return bc->get(key, keySize, value, valueSize);
163 }
164 return 0;
165}
166
Jamie Gennis98c63832011-11-07 17:03:54 -0800167void egl_cache_t::setCacheFilename(const char* filename) {
Mathias Agopian65421432017-03-08 11:49:05 -0800168 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis98c63832011-11-07 17:03:54 -0800169 mFilename = filename;
170}
171
Jamie Gennis76601082011-11-06 14:14:33 -0800172sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
173 if (mBlobCache == NULL) {
174 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
175 loadBlobCacheLocked();
176 }
177 return mBlobCache;
178}
179
Jamie Gennis98c63832011-11-07 17:03:54 -0800180static uint32_t crc32c(const uint8_t* buf, size_t len) {
181 const uint32_t polyBits = 0x82F63B78;
182 uint32_t r = 0;
183 for (size_t i = 0; i < len; i++) {
184 r ^= buf[i];
185 for (int j = 0; j < 8; j++) {
186 if (r & 1) {
187 r = (r >> 1) ^ polyBits;
188 } else {
189 r >>= 1;
190 }
191 }
192 }
193 return r;
194}
195
Jamie Gennis76601082011-11-06 14:14:33 -0800196void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis5539e212013-07-30 15:10:02 -0700197 if (mFilename.length() > 0 && mBlobCache != NULL) {
Jamie Gennis98c63832011-11-07 17:03:54 -0800198 size_t cacheSize = mBlobCache->getFlattenedSize();
199 size_t headerSize = cacheFileHeaderSize;
Mathias Agopian65421432017-03-08 11:49:05 -0800200 const char* fname = mFilename.c_str();
Jamie Gennis98c63832011-11-07 17:03:54 -0800201
202 // Try to create the file with no permissions so we can write it
203 // without anyone trying to read it.
204 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
205 if (fd == -1) {
206 if (errno == EEXIST) {
207 // The file exists, delete it and try again.
208 if (unlink(fname) == -1) {
209 // No point in retrying if the unlink failed.
Steve Blocke6f43dd2012-01-06 19:20:56 +0000210 ALOGE("error unlinking cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800211 strerror(errno), errno);
212 return;
213 }
214 // Retry now that we've unlinked the file.
215 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
216 }
217 if (fd == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000218 ALOGE("error creating cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800219 strerror(errno), errno);
220 return;
221 }
222 }
223
224 size_t fileSize = headerSize + cacheSize;
Jamie Gennis98c63832011-11-07 17:03:54 -0800225
vijay guptaa30cc7d2012-06-27 16:14:42 -0700226 uint8_t* buf = new uint8_t [fileSize];
227 if (!buf) {
228 ALOGE("error allocating buffer for cache contents: %s (%d)",
229 strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800230 close(fd);
231 unlink(fname);
232 return;
233 }
234
Mathias Agopian65421432017-03-08 11:49:05 -0800235 int err = mBlobCache->flatten(buf + headerSize, cacheSize);
236 if (err < 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000237 ALOGE("error writing cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800238 -err);
vijay guptaa30cc7d2012-06-27 16:14:42 -0700239 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800240 close(fd);
241 unlink(fname);
242 return;
243 }
244
245 // Write the file magic and CRC
246 memcpy(buf, cacheFileMagic, 4);
247 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
248 *crc = crc32c(buf + headerSize, cacheSize);
249
vijay guptaa30cc7d2012-06-27 16:14:42 -0700250 if (write(fd, buf, fileSize) == -1) {
251 ALOGE("error writing cache file: %s (%d)", strerror(errno),
252 errno);
253 delete [] buf;
254 close(fd);
255 unlink(fname);
256 return;
257 }
258
259 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800260 fchmod(fd, S_IRUSR);
261 close(fd);
262 }
Jamie Gennis76601082011-11-06 14:14:33 -0800263}
264
265void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800266 if (mFilename.length() > 0) {
267 size_t headerSize = cacheFileHeaderSize;
268
Mathias Agopian65421432017-03-08 11:49:05 -0800269 int fd = open(mFilename.c_str(), O_RDONLY, 0);
Jamie Gennis98c63832011-11-07 17:03:54 -0800270 if (fd == -1) {
271 if (errno != ENOENT) {
Mathias Agopian65421432017-03-08 11:49:05 -0800272 ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
Jamie Gennis98c63832011-11-07 17:03:54 -0800273 strerror(errno), errno);
274 }
275 return;
276 }
277
278 struct stat statBuf;
279 if (fstat(fd, &statBuf) == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000280 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800281 close(fd);
282 return;
283 }
284
285 // Sanity check the size before trying to mmap it.
286 size_t fileSize = statBuf.st_size;
287 if (fileSize > maxTotalSize * 2) {
Dan Alberteacd31f2016-02-02 15:08:34 -0800288 ALOGE("cache file is too large: %#" PRIx64,
289 static_cast<off64_t>(statBuf.st_size));
Jamie Gennis98c63832011-11-07 17:03:54 -0800290 close(fd);
291 return;
292 }
293
294 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
295 PROT_READ, MAP_PRIVATE, fd, 0));
296 if (buf == MAP_FAILED) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000297 ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
Jamie Gennis98c63832011-11-07 17:03:54 -0800298 errno);
299 close(fd);
300 return;
301 }
302
303 // Check the file magic and CRC
304 size_t cacheSize = fileSize - headerSize;
305 if (memcmp(buf, cacheFileMagic, 4) != 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000306 ALOGE("cache file has bad mojo");
Jamie Gennis98c63832011-11-07 17:03:54 -0800307 close(fd);
308 return;
309 }
310 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
311 if (crc32c(buf + headerSize, cacheSize) != *crc) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000312 ALOGE("cache file failed CRC check");
Jamie Gennis98c63832011-11-07 17:03:54 -0800313 close(fd);
314 return;
315 }
316
Mathias Agopian65421432017-03-08 11:49:05 -0800317 int err = mBlobCache->unflatten(buf + headerSize, cacheSize);
318 if (err < 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000319 ALOGE("error reading cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800320 -err);
321 munmap(buf, fileSize);
322 close(fd);
323 return;
324 }
325
326 munmap(buf, fileSize);
327 close(fd);
328 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700329}
330
331// ----------------------------------------------------------------------------
332}; // namespace android
333// ----------------------------------------------------------------------------