blob: dc1a4af46e133fa64e0df19d5f61a0b860a76ae6 [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
Mathias Agopianb7f9a242017-03-08 22:29:31 -080023
24#include <private/EGL/cache.h>
25
Dan Alberteacd31f2016-02-02 15:08:34 -080026#include <inttypes.h>
Jamie Gennis98c63832011-11-07 17:03:54 -080027#include <sys/mman.h>
28#include <sys/stat.h>
Mathias Agopian311b4792017-02-28 15:00:49 -080029
Mathias Agopian65421432017-03-08 11:49:05 -080030#include <thread>
31
32#include <log/log.h>
Jamie Gennis98c63832011-11-07 17:03:54 -080033
Jamie Gennis76601082011-11-06 14:14:33 -080034// Cache size limits.
Dan Willemsenbace39e2016-10-11 15:42:59 -070035static const size_t maxKeySize = 12 * 1024;
36static const size_t maxValueSize = 64 * 1024;
37static const size_t maxTotalSize = 2 * 1024 * 1024;
Jamie Gennis76601082011-11-06 14:14:33 -080038
Jamie Gennis98c63832011-11-07 17:03:54 -080039// Cache file header
40static const char* cacheFileMagic = "EGL$";
41static const size_t cacheFileHeaderSize = 8;
42
Jamie Gennis99c3d702011-11-08 17:59:36 -080043// The time in seconds to wait before saving newly inserted cache entries.
44static const unsigned int deferredSaveDelay = 4;
45
Jamie Gennisaca51c02011-11-03 17:42:43 -070046// ----------------------------------------------------------------------------
47namespace android {
48// ----------------------------------------------------------------------------
49
50#define BC_EXT_STR "EGL_ANDROID_blob_cache"
51
Mathias Agopianb7f9a242017-03-08 22:29:31 -080052// called from android_view_ThreadedRenderer.cpp
53void egl_set_cache_filename(const char* filename) {
54 egl_cache_t::get()->setCacheFilename(filename);
55}
56
Jamie Gennisaca51c02011-11-03 17:42:43 -070057//
Jamie Gennis76601082011-11-06 14:14:33 -080058// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070059//
Jamie Gennisc42fcf02011-11-09 15:35:34 -080060static void setBlob(const void* key, EGLsizeiANDROID keySize,
61 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080062 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070063}
64
Jamie Gennisc42fcf02011-11-09 15:35:34 -080065static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
66 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080067 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
68}
69
70//
71// egl_cache_t definition
72//
73egl_cache_t::egl_cache_t() :
Mathias Agopianb7f9a242017-03-08 22:29:31 -080074 mInitialized(false) {
Jamie Gennis76601082011-11-06 14:14:33 -080075}
76
77egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070078}
79
Jamie Gennis98c63832011-11-07 17:03:54 -080080egl_cache_t egl_cache_t::sCache;
81
Jamie Gennisaca51c02011-11-03 17:42:43 -070082egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080083 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070084}
85
86void egl_cache_t::initialize(egl_display_t *display) {
Mathias Agopian65421432017-03-08 11:49:05 -080087 std::lock_guard<std::mutex> lock(mMutex);
Mathias Agopianada798b2012-02-13 17:09:30 -080088
89 egl_connection_t* const cnx = &gEGLImpl;
90 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
91 const char* exts = display->disp.queryString.extensions;
92 size_t bcExtLen = strlen(BC_EXT_STR);
93 size_t extsLen = strlen(exts);
94 bool equal = !strcmp(BC_EXT_STR, exts);
95 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
96 bool atEnd = (bcExtLen+1) < extsLen &&
97 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
Mathias Agopian311b4792017-02-28 15:00:49 -080098 bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
Mathias Agopianada798b2012-02-13 17:09:30 -080099 if (equal || atStart || atEnd || inMiddle) {
100 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
101 eglSetBlobCacheFuncsANDROID =
102 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800103 cnx->egl.eglGetProcAddress(
104 "eglSetBlobCacheFuncsANDROID"));
Mathias Agopianada798b2012-02-13 17:09:30 -0800105 if (eglSetBlobCacheFuncsANDROID == NULL) {
106 ALOGE("EGL_ANDROID_blob_cache advertised, "
107 "but unable to get eglSetBlobCacheFuncsANDROID");
108 return;
109 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700110
Mathias Agopianada798b2012-02-13 17:09:30 -0800111 eglSetBlobCacheFuncsANDROID(display->disp.dpy,
112 android::setBlob, android::getBlob);
113 EGLint err = cnx->egl.eglGetError();
114 if (err != EGL_SUCCESS) {
115 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
116 "%#x", err);
Jamie Gennisaca51c02011-11-03 17:42:43 -0700117 }
118 }
119 }
Mathias Agopianada798b2012-02-13 17:09:30 -0800120
Jamie Gennis76601082011-11-06 14:14:33 -0800121 mInitialized = true;
122}
123
124void egl_cache_t::terminate() {
Mathias Agopian65421432017-03-08 11:49:05 -0800125 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis5539e212013-07-30 15:10:02 -0700126 saveBlobCacheLocked();
127 mBlobCache = NULL;
Jamie Gennis76601082011-11-06 14:14:33 -0800128}
129
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800130void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
131 const void* value, EGLsizeiANDROID valueSize) {
Mathias Agopian65421432017-03-08 11:49:05 -0800132 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis76601082011-11-06 14:14:33 -0800133
134 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000135 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800136 return;
137 }
138
139 if (mInitialized) {
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800140 BlobCache* bc = getBlobCacheLocked();
Jamie Gennis76601082011-11-06 14:14:33 -0800141 bc->set(key, keySize, value, valueSize);
Jamie Gennis99c3d702011-11-08 17:59:36 -0800142
143 if (!mSavePending) {
Jamie Gennis99c3d702011-11-08 17:59:36 -0800144 mSavePending = true;
Mathias Agopian65421432017-03-08 11:49:05 -0800145 std::thread deferredSaveThread([this]() {
146 sleep(deferredSaveDelay);
147 std::lock_guard<std::mutex> lock(mMutex);
148 if (mInitialized) {
149 saveBlobCacheLocked();
150 }
151 mSavePending = false;
152 });
153 deferredSaveThread.detach();
Jamie Gennis99c3d702011-11-08 17:59:36 -0800154 }
Jamie Gennis76601082011-11-06 14:14:33 -0800155 }
156}
157
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800158EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
159 void* value, EGLsizeiANDROID valueSize) {
Mathias Agopian65421432017-03-08 11:49:05 -0800160 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis76601082011-11-06 14:14:33 -0800161
162 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000163 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800164 return 0;
165 }
166
167 if (mInitialized) {
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800168 BlobCache* bc = getBlobCacheLocked();
Jamie Gennis76601082011-11-06 14:14:33 -0800169 return bc->get(key, keySize, value, valueSize);
170 }
171 return 0;
172}
173
Jamie Gennis98c63832011-11-07 17:03:54 -0800174void egl_cache_t::setCacheFilename(const char* filename) {
Mathias Agopian65421432017-03-08 11:49:05 -0800175 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis98c63832011-11-07 17:03:54 -0800176 mFilename = filename;
177}
178
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800179BlobCache* egl_cache_t::getBlobCacheLocked() {
180 if (mBlobCache == nullptr) {
181 mBlobCache.reset(new BlobCache(maxKeySize, maxValueSize, maxTotalSize));
Jamie Gennis76601082011-11-06 14:14:33 -0800182 loadBlobCacheLocked();
183 }
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800184 return mBlobCache.get();
Jamie Gennis76601082011-11-06 14:14:33 -0800185}
186
Jamie Gennis98c63832011-11-07 17:03:54 -0800187static uint32_t crc32c(const uint8_t* buf, size_t len) {
188 const uint32_t polyBits = 0x82F63B78;
189 uint32_t r = 0;
190 for (size_t i = 0; i < len; i++) {
191 r ^= buf[i];
192 for (int j = 0; j < 8; j++) {
193 if (r & 1) {
194 r = (r >> 1) ^ polyBits;
195 } else {
196 r >>= 1;
197 }
198 }
199 }
200 return r;
201}
202
Jamie Gennis76601082011-11-06 14:14:33 -0800203void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis5539e212013-07-30 15:10:02 -0700204 if (mFilename.length() > 0 && mBlobCache != NULL) {
Jamie Gennis98c63832011-11-07 17:03:54 -0800205 size_t cacheSize = mBlobCache->getFlattenedSize();
206 size_t headerSize = cacheFileHeaderSize;
Mathias Agopian65421432017-03-08 11:49:05 -0800207 const char* fname = mFilename.c_str();
Jamie Gennis98c63832011-11-07 17:03:54 -0800208
209 // Try to create the file with no permissions so we can write it
210 // without anyone trying to read it.
211 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
212 if (fd == -1) {
213 if (errno == EEXIST) {
214 // The file exists, delete it and try again.
215 if (unlink(fname) == -1) {
216 // No point in retrying if the unlink failed.
Steve Blocke6f43dd2012-01-06 19:20:56 +0000217 ALOGE("error unlinking cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800218 strerror(errno), errno);
219 return;
220 }
221 // Retry now that we've unlinked the file.
222 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
223 }
224 if (fd == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000225 ALOGE("error creating cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800226 strerror(errno), errno);
227 return;
228 }
229 }
230
231 size_t fileSize = headerSize + cacheSize;
Jamie Gennis98c63832011-11-07 17:03:54 -0800232
vijay guptaa30cc7d2012-06-27 16:14:42 -0700233 uint8_t* buf = new uint8_t [fileSize];
234 if (!buf) {
235 ALOGE("error allocating buffer for cache contents: %s (%d)",
236 strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800237 close(fd);
238 unlink(fname);
239 return;
240 }
241
Mathias Agopian65421432017-03-08 11:49:05 -0800242 int err = mBlobCache->flatten(buf + headerSize, cacheSize);
243 if (err < 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000244 ALOGE("error writing cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800245 -err);
vijay guptaa30cc7d2012-06-27 16:14:42 -0700246 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800247 close(fd);
248 unlink(fname);
249 return;
250 }
251
252 // Write the file magic and CRC
253 memcpy(buf, cacheFileMagic, 4);
254 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
255 *crc = crc32c(buf + headerSize, cacheSize);
256
vijay guptaa30cc7d2012-06-27 16:14:42 -0700257 if (write(fd, buf, fileSize) == -1) {
258 ALOGE("error writing cache file: %s (%d)", strerror(errno),
259 errno);
260 delete [] buf;
261 close(fd);
262 unlink(fname);
263 return;
264 }
265
266 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800267 fchmod(fd, S_IRUSR);
268 close(fd);
269 }
Jamie Gennis76601082011-11-06 14:14:33 -0800270}
271
272void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800273 if (mFilename.length() > 0) {
274 size_t headerSize = cacheFileHeaderSize;
275
Mathias Agopian65421432017-03-08 11:49:05 -0800276 int fd = open(mFilename.c_str(), O_RDONLY, 0);
Jamie Gennis98c63832011-11-07 17:03:54 -0800277 if (fd == -1) {
278 if (errno != ENOENT) {
Mathias Agopian65421432017-03-08 11:49:05 -0800279 ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
Jamie Gennis98c63832011-11-07 17:03:54 -0800280 strerror(errno), errno);
281 }
282 return;
283 }
284
285 struct stat statBuf;
286 if (fstat(fd, &statBuf) == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000287 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800288 close(fd);
289 return;
290 }
291
292 // Sanity check the size before trying to mmap it.
293 size_t fileSize = statBuf.st_size;
294 if (fileSize > maxTotalSize * 2) {
Dan Alberteacd31f2016-02-02 15:08:34 -0800295 ALOGE("cache file is too large: %#" PRIx64,
296 static_cast<off64_t>(statBuf.st_size));
Jamie Gennis98c63832011-11-07 17:03:54 -0800297 close(fd);
298 return;
299 }
300
301 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
302 PROT_READ, MAP_PRIVATE, fd, 0));
303 if (buf == MAP_FAILED) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000304 ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
Jamie Gennis98c63832011-11-07 17:03:54 -0800305 errno);
306 close(fd);
307 return;
308 }
309
310 // Check the file magic and CRC
311 size_t cacheSize = fileSize - headerSize;
312 if (memcmp(buf, cacheFileMagic, 4) != 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000313 ALOGE("cache file has bad mojo");
Jamie Gennis98c63832011-11-07 17:03:54 -0800314 close(fd);
315 return;
316 }
317 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
318 if (crc32c(buf + headerSize, cacheSize) != *crc) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000319 ALOGE("cache file failed CRC check");
Jamie Gennis98c63832011-11-07 17:03:54 -0800320 close(fd);
321 return;
322 }
323
Mathias Agopian65421432017-03-08 11:49:05 -0800324 int err = mBlobCache->unflatten(buf + headerSize, cacheSize);
325 if (err < 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000326 ALOGE("error reading cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800327 -err);
328 munmap(buf, fileSize);
329 close(fd);
330 return;
331 }
332
333 munmap(buf, fileSize);
334 close(fd);
335 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700336}
337
338// ----------------------------------------------------------------------------
339}; // namespace android
340// ----------------------------------------------------------------------------