blob: 579e422c1af2e5e6b196e5fb67887c431abaa948 [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>
Jiyong Parka243e5d2017-06-21 12:26:51 +090029#include <unistd.h>
Mathias Agopian311b4792017-02-28 15:00:49 -080030
Mathias Agopian65421432017-03-08 11:49:05 -080031#include <thread>
32
33#include <log/log.h>
Jamie Gennis98c63832011-11-07 17:03:54 -080034
Jamie Gennis76601082011-11-06 14:14:33 -080035// Cache size limits.
Dan Willemsenbace39e2016-10-11 15:42:59 -070036static const size_t maxKeySize = 12 * 1024;
37static const size_t maxValueSize = 64 * 1024;
38static const size_t maxTotalSize = 2 * 1024 * 1024;
Jamie Gennis76601082011-11-06 14:14:33 -080039
Jamie Gennis98c63832011-11-07 17:03:54 -080040// Cache file header
41static const char* cacheFileMagic = "EGL$";
42static const size_t cacheFileHeaderSize = 8;
43
Jamie Gennis99c3d702011-11-08 17:59:36 -080044// The time in seconds to wait before saving newly inserted cache entries.
45static const unsigned int deferredSaveDelay = 4;
46
Jamie Gennisaca51c02011-11-03 17:42:43 -070047// ----------------------------------------------------------------------------
48namespace android {
49// ----------------------------------------------------------------------------
50
51#define BC_EXT_STR "EGL_ANDROID_blob_cache"
52
Mathias Agopianb7f9a242017-03-08 22:29:31 -080053// called from android_view_ThreadedRenderer.cpp
54void egl_set_cache_filename(const char* filename) {
55 egl_cache_t::get()->setCacheFilename(filename);
56}
57
Jamie Gennisaca51c02011-11-03 17:42:43 -070058//
Jamie Gennis76601082011-11-06 14:14:33 -080059// Callback functions passed to EGL.
Jamie Gennisaca51c02011-11-03 17:42:43 -070060//
Jamie Gennisc42fcf02011-11-09 15:35:34 -080061static void setBlob(const void* key, EGLsizeiANDROID keySize,
62 const void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080063 egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
Jamie Gennisaca51c02011-11-03 17:42:43 -070064}
65
Jamie Gennisc42fcf02011-11-09 15:35:34 -080066static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
67 void* value, EGLsizeiANDROID valueSize) {
Jamie Gennis76601082011-11-06 14:14:33 -080068 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
69}
70
71//
72// egl_cache_t definition
73//
74egl_cache_t::egl_cache_t() :
Mathias Agopianb7f9a242017-03-08 22:29:31 -080075 mInitialized(false) {
Jamie Gennis76601082011-11-06 14:14:33 -080076}
77
78egl_cache_t::~egl_cache_t() {
Jamie Gennisaca51c02011-11-03 17:42:43 -070079}
80
Jamie Gennis98c63832011-11-07 17:03:54 -080081egl_cache_t egl_cache_t::sCache;
82
Jamie Gennisaca51c02011-11-03 17:42:43 -070083egl_cache_t* egl_cache_t::get() {
Jamie Gennis98c63832011-11-07 17:03:54 -080084 return &sCache;
Jamie Gennisaca51c02011-11-03 17:42:43 -070085}
86
87void egl_cache_t::initialize(egl_display_t *display) {
Mathias Agopian65421432017-03-08 11:49:05 -080088 std::lock_guard<std::mutex> lock(mMutex);
Mathias Agopianada798b2012-02-13 17:09:30 -080089
90 egl_connection_t* const cnx = &gEGLImpl;
91 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
92 const char* exts = display->disp.queryString.extensions;
93 size_t bcExtLen = strlen(BC_EXT_STR);
94 size_t extsLen = strlen(exts);
95 bool equal = !strcmp(BC_EXT_STR, exts);
96 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
97 bool atEnd = (bcExtLen+1) < extsLen &&
98 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
Mathias Agopian311b4792017-02-28 15:00:49 -080099 bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
Mathias Agopianada798b2012-02-13 17:09:30 -0800100 if (equal || atStart || atEnd || inMiddle) {
101 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
102 eglSetBlobCacheFuncsANDROID =
103 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800104 cnx->egl.eglGetProcAddress(
105 "eglSetBlobCacheFuncsANDROID"));
Mathias Agopianada798b2012-02-13 17:09:30 -0800106 if (eglSetBlobCacheFuncsANDROID == NULL) {
107 ALOGE("EGL_ANDROID_blob_cache advertised, "
108 "but unable to get eglSetBlobCacheFuncsANDROID");
109 return;
110 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700111
Mathias Agopianada798b2012-02-13 17:09:30 -0800112 eglSetBlobCacheFuncsANDROID(display->disp.dpy,
113 android::setBlob, android::getBlob);
114 EGLint err = cnx->egl.eglGetError();
115 if (err != EGL_SUCCESS) {
116 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
117 "%#x", err);
Jamie Gennisaca51c02011-11-03 17:42:43 -0700118 }
119 }
120 }
Mathias Agopianada798b2012-02-13 17:09:30 -0800121
Jamie Gennis76601082011-11-06 14:14:33 -0800122 mInitialized = true;
123}
124
125void egl_cache_t::terminate() {
Mathias Agopian65421432017-03-08 11:49:05 -0800126 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis5539e212013-07-30 15:10:02 -0700127 saveBlobCacheLocked();
128 mBlobCache = NULL;
Jamie Gennis76601082011-11-06 14:14:33 -0800129}
130
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800131void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
132 const void* value, EGLsizeiANDROID valueSize) {
Mathias Agopian65421432017-03-08 11:49:05 -0800133 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis76601082011-11-06 14:14:33 -0800134
135 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000136 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800137 return;
138 }
139
140 if (mInitialized) {
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800141 BlobCache* bc = getBlobCacheLocked();
Jamie Gennis76601082011-11-06 14:14:33 -0800142 bc->set(key, keySize, value, valueSize);
Jamie Gennis99c3d702011-11-08 17:59:36 -0800143
144 if (!mSavePending) {
Jamie Gennis99c3d702011-11-08 17:59:36 -0800145 mSavePending = true;
Mathias Agopian65421432017-03-08 11:49:05 -0800146 std::thread deferredSaveThread([this]() {
147 sleep(deferredSaveDelay);
148 std::lock_guard<std::mutex> lock(mMutex);
149 if (mInitialized) {
150 saveBlobCacheLocked();
151 }
152 mSavePending = false;
153 });
154 deferredSaveThread.detach();
Jamie Gennis99c3d702011-11-08 17:59:36 -0800155 }
Jamie Gennis76601082011-11-06 14:14:33 -0800156 }
157}
158
Jamie Gennisc42fcf02011-11-09 15:35:34 -0800159EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
160 void* value, EGLsizeiANDROID valueSize) {
Mathias Agopian65421432017-03-08 11:49:05 -0800161 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis76601082011-11-06 14:14:33 -0800162
163 if (keySize < 0 || valueSize < 0) {
Steve Block32397c12012-01-05 23:22:43 +0000164 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
Jamie Gennis76601082011-11-06 14:14:33 -0800165 return 0;
166 }
167
168 if (mInitialized) {
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800169 BlobCache* bc = getBlobCacheLocked();
Jamie Gennis76601082011-11-06 14:14:33 -0800170 return bc->get(key, keySize, value, valueSize);
171 }
172 return 0;
173}
174
Jamie Gennis98c63832011-11-07 17:03:54 -0800175void egl_cache_t::setCacheFilename(const char* filename) {
Mathias Agopian65421432017-03-08 11:49:05 -0800176 std::lock_guard<std::mutex> lock(mMutex);
Jamie Gennis98c63832011-11-07 17:03:54 -0800177 mFilename = filename;
178}
179
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800180BlobCache* egl_cache_t::getBlobCacheLocked() {
181 if (mBlobCache == nullptr) {
182 mBlobCache.reset(new BlobCache(maxKeySize, maxValueSize, maxTotalSize));
Jamie Gennis76601082011-11-06 14:14:33 -0800183 loadBlobCacheLocked();
184 }
Mathias Agopianb7f9a242017-03-08 22:29:31 -0800185 return mBlobCache.get();
Jamie Gennis76601082011-11-06 14:14:33 -0800186}
187
Jamie Gennis98c63832011-11-07 17:03:54 -0800188static uint32_t crc32c(const uint8_t* buf, size_t len) {
189 const uint32_t polyBits = 0x82F63B78;
190 uint32_t r = 0;
191 for (size_t i = 0; i < len; i++) {
192 r ^= buf[i];
193 for (int j = 0; j < 8; j++) {
194 if (r & 1) {
195 r = (r >> 1) ^ polyBits;
196 } else {
197 r >>= 1;
198 }
199 }
200 }
201 return r;
202}
203
Jamie Gennis76601082011-11-06 14:14:33 -0800204void egl_cache_t::saveBlobCacheLocked() {
Jamie Gennis5539e212013-07-30 15:10:02 -0700205 if (mFilename.length() > 0 && mBlobCache != NULL) {
Jamie Gennis98c63832011-11-07 17:03:54 -0800206 size_t cacheSize = mBlobCache->getFlattenedSize();
207 size_t headerSize = cacheFileHeaderSize;
Mathias Agopian65421432017-03-08 11:49:05 -0800208 const char* fname = mFilename.c_str();
Jamie Gennis98c63832011-11-07 17:03:54 -0800209
210 // Try to create the file with no permissions so we can write it
211 // without anyone trying to read it.
212 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
213 if (fd == -1) {
214 if (errno == EEXIST) {
215 // The file exists, delete it and try again.
216 if (unlink(fname) == -1) {
217 // No point in retrying if the unlink failed.
Steve Blocke6f43dd2012-01-06 19:20:56 +0000218 ALOGE("error unlinking cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800219 strerror(errno), errno);
220 return;
221 }
222 // Retry now that we've unlinked the file.
223 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
224 }
225 if (fd == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000226 ALOGE("error creating cache file %s: %s (%d)", fname,
Jamie Gennis98c63832011-11-07 17:03:54 -0800227 strerror(errno), errno);
228 return;
229 }
230 }
231
232 size_t fileSize = headerSize + cacheSize;
Jamie Gennis98c63832011-11-07 17:03:54 -0800233
vijay guptaa30cc7d2012-06-27 16:14:42 -0700234 uint8_t* buf = new uint8_t [fileSize];
235 if (!buf) {
236 ALOGE("error allocating buffer for cache contents: %s (%d)",
237 strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800238 close(fd);
239 unlink(fname);
240 return;
241 }
242
Mathias Agopian65421432017-03-08 11:49:05 -0800243 int err = mBlobCache->flatten(buf + headerSize, cacheSize);
244 if (err < 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000245 ALOGE("error writing cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800246 -err);
vijay guptaa30cc7d2012-06-27 16:14:42 -0700247 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800248 close(fd);
249 unlink(fname);
250 return;
251 }
252
253 // Write the file magic and CRC
254 memcpy(buf, cacheFileMagic, 4);
255 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
256 *crc = crc32c(buf + headerSize, cacheSize);
257
vijay guptaa30cc7d2012-06-27 16:14:42 -0700258 if (write(fd, buf, fileSize) == -1) {
259 ALOGE("error writing cache file: %s (%d)", strerror(errno),
260 errno);
261 delete [] buf;
262 close(fd);
263 unlink(fname);
264 return;
265 }
266
267 delete [] buf;
Jamie Gennis98c63832011-11-07 17:03:54 -0800268 fchmod(fd, S_IRUSR);
269 close(fd);
270 }
Jamie Gennis76601082011-11-06 14:14:33 -0800271}
272
273void egl_cache_t::loadBlobCacheLocked() {
Jamie Gennis98c63832011-11-07 17:03:54 -0800274 if (mFilename.length() > 0) {
275 size_t headerSize = cacheFileHeaderSize;
276
Mathias Agopian65421432017-03-08 11:49:05 -0800277 int fd = open(mFilename.c_str(), O_RDONLY, 0);
Jamie Gennis98c63832011-11-07 17:03:54 -0800278 if (fd == -1) {
279 if (errno != ENOENT) {
Mathias Agopian65421432017-03-08 11:49:05 -0800280 ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
Jamie Gennis98c63832011-11-07 17:03:54 -0800281 strerror(errno), errno);
282 }
283 return;
284 }
285
286 struct stat statBuf;
287 if (fstat(fd, &statBuf) == -1) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000288 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
Jamie Gennis98c63832011-11-07 17:03:54 -0800289 close(fd);
290 return;
291 }
292
293 // Sanity check the size before trying to mmap it.
294 size_t fileSize = statBuf.st_size;
295 if (fileSize > maxTotalSize * 2) {
Dan Alberteacd31f2016-02-02 15:08:34 -0800296 ALOGE("cache file is too large: %#" PRIx64,
297 static_cast<off64_t>(statBuf.st_size));
Jamie Gennis98c63832011-11-07 17:03:54 -0800298 close(fd);
299 return;
300 }
301
302 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
303 PROT_READ, MAP_PRIVATE, fd, 0));
304 if (buf == MAP_FAILED) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000305 ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
Jamie Gennis98c63832011-11-07 17:03:54 -0800306 errno);
307 close(fd);
308 return;
309 }
310
311 // Check the file magic and CRC
312 size_t cacheSize = fileSize - headerSize;
313 if (memcmp(buf, cacheFileMagic, 4) != 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000314 ALOGE("cache file has bad mojo");
Jamie Gennis98c63832011-11-07 17:03:54 -0800315 close(fd);
316 return;
317 }
318 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
319 if (crc32c(buf + headerSize, cacheSize) != *crc) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000320 ALOGE("cache file failed CRC check");
Jamie Gennis98c63832011-11-07 17:03:54 -0800321 close(fd);
322 return;
323 }
324
Mathias Agopian65421432017-03-08 11:49:05 -0800325 int err = mBlobCache->unflatten(buf + headerSize, cacheSize);
326 if (err < 0) {
Steve Blocke6f43dd2012-01-06 19:20:56 +0000327 ALOGE("error reading cache contents: %s (%d)", strerror(-err),
Jamie Gennis98c63832011-11-07 17:03:54 -0800328 -err);
329 munmap(buf, fileSize);
330 close(fd);
331 return;
332 }
333
334 munmap(buf, fileSize);
335 close(fd);
336 }
Jamie Gennisaca51c02011-11-03 17:42:43 -0700337}
338
339// ----------------------------------------------------------------------------
340}; // namespace android
341// ----------------------------------------------------------------------------