egl_cache.cpp revision 5539e219de5ffa93e9f22b30dacf7c28e7f7a0be
1/* 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_impl.h" 18 19#include "egl_cache.h" 20#include "egl_display.h" 21#include "egldefs.h" 22 23#include <fcntl.h> 24#include <sys/mman.h> 25#include <sys/stat.h> 26#include <sys/types.h> 27#include <unistd.h> 28 29#ifndef MAX_EGL_CACHE_ENTRY_SIZE 30#define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024); 31#endif 32 33#ifndef MAX_EGL_CACHE_KEY_SIZE 34#define MAX_EGL_CACHE_KEY_SIZE (1024); 35#endif 36 37#ifndef MAX_EGL_CACHE_SIZE 38#define MAX_EGL_CACHE_SIZE (64 * 1024); 39#endif 40 41// Cache size limits. 42static const size_t maxKeySize = MAX_EGL_CACHE_KEY_SIZE; 43static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE; 44static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE; 45 46// Cache file header 47static const char* cacheFileMagic = "EGL$"; 48static const size_t cacheFileHeaderSize = 8; 49 50// The time in seconds to wait before saving newly inserted cache entries. 51static const unsigned int deferredSaveDelay = 4; 52 53// ---------------------------------------------------------------------------- 54namespace android { 55// ---------------------------------------------------------------------------- 56 57#define BC_EXT_STR "EGL_ANDROID_blob_cache" 58 59// 60// Callback functions passed to EGL. 61// 62static void setBlob(const void* key, EGLsizeiANDROID keySize, 63 const void* value, EGLsizeiANDROID valueSize) { 64 egl_cache_t::get()->setBlob(key, keySize, value, valueSize); 65} 66 67static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, 68 void* value, EGLsizeiANDROID valueSize) { 69 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize); 70} 71 72// 73// egl_cache_t definition 74// 75egl_cache_t::egl_cache_t() : 76 mInitialized(false), 77 mBlobCache(NULL) { 78} 79 80egl_cache_t::~egl_cache_t() { 81} 82 83egl_cache_t egl_cache_t::sCache; 84 85egl_cache_t* egl_cache_t::get() { 86 return &sCache; 87} 88 89void egl_cache_t::initialize(egl_display_t *display) { 90 Mutex::Autolock lock(mMutex); 91 92 egl_connection_t* const cnx = &gEGLImpl; 93 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { 94 const char* exts = display->disp.queryString.extensions; 95 size_t bcExtLen = strlen(BC_EXT_STR); 96 size_t extsLen = strlen(exts); 97 bool equal = !strcmp(BC_EXT_STR, exts); 98 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1); 99 bool atEnd = (bcExtLen+1) < extsLen && 100 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1)); 101 bool inMiddle = strstr(exts, " " BC_EXT_STR " "); 102 if (equal || atStart || atEnd || inMiddle) { 103 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID; 104 eglSetBlobCacheFuncsANDROID = 105 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( 106 cnx->egl.eglGetProcAddress( 107 "eglSetBlobCacheFuncsANDROID")); 108 if (eglSetBlobCacheFuncsANDROID == NULL) { 109 ALOGE("EGL_ANDROID_blob_cache advertised, " 110 "but unable to get eglSetBlobCacheFuncsANDROID"); 111 return; 112 } 113 114 eglSetBlobCacheFuncsANDROID(display->disp.dpy, 115 android::setBlob, android::getBlob); 116 EGLint err = cnx->egl.eglGetError(); 117 if (err != EGL_SUCCESS) { 118 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: " 119 "%#x", err); 120 } 121 } 122 } 123 124 mInitialized = true; 125} 126 127void egl_cache_t::terminate() { 128 Mutex::Autolock lock(mMutex); 129 saveBlobCacheLocked(); 130 mBlobCache = NULL; 131} 132 133void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, 134 const void* value, EGLsizeiANDROID valueSize) { 135 Mutex::Autolock lock(mMutex); 136 137 if (keySize < 0 || valueSize < 0) { 138 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); 139 return; 140 } 141 142 if (mInitialized) { 143 sp<BlobCache> bc = getBlobCacheLocked(); 144 bc->set(key, keySize, value, valueSize); 145 146 if (!mSavePending) { 147 class DeferredSaveThread : public Thread { 148 public: 149 DeferredSaveThread() : Thread(false) {} 150 151 virtual bool threadLoop() { 152 sleep(deferredSaveDelay); 153 egl_cache_t* c = egl_cache_t::get(); 154 Mutex::Autolock lock(c->mMutex); 155 if (c->mInitialized) { 156 c->saveBlobCacheLocked(); 157 } 158 c->mSavePending = false; 159 return false; 160 } 161 }; 162 163 // The thread will hold a strong ref to itself until it has finished 164 // running, so there's no need to keep a ref around. 165 sp<Thread> deferredSaveThread(new DeferredSaveThread()); 166 mSavePending = true; 167 deferredSaveThread->run(); 168 } 169 } 170} 171 172EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, 173 void* value, EGLsizeiANDROID valueSize) { 174 Mutex::Autolock lock(mMutex); 175 176 if (keySize < 0 || valueSize < 0) { 177 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); 178 return 0; 179 } 180 181 if (mInitialized) { 182 sp<BlobCache> bc = getBlobCacheLocked(); 183 return bc->get(key, keySize, value, valueSize); 184 } 185 return 0; 186} 187 188void egl_cache_t::setCacheFilename(const char* filename) { 189 Mutex::Autolock lock(mMutex); 190 mFilename = filename; 191} 192 193sp<BlobCache> egl_cache_t::getBlobCacheLocked() { 194 if (mBlobCache == NULL) { 195 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize); 196 loadBlobCacheLocked(); 197 } 198 return mBlobCache; 199} 200 201static uint32_t crc32c(const uint8_t* buf, size_t len) { 202 const uint32_t polyBits = 0x82F63B78; 203 uint32_t r = 0; 204 for (size_t i = 0; i < len; i++) { 205 r ^= buf[i]; 206 for (int j = 0; j < 8; j++) { 207 if (r & 1) { 208 r = (r >> 1) ^ polyBits; 209 } else { 210 r >>= 1; 211 } 212 } 213 } 214 return r; 215} 216 217void egl_cache_t::saveBlobCacheLocked() { 218 if (mFilename.length() > 0 && mBlobCache != NULL) { 219 size_t cacheSize = mBlobCache->getFlattenedSize(); 220 size_t headerSize = cacheFileHeaderSize; 221 const char* fname = mFilename.string(); 222 223 // Try to create the file with no permissions so we can write it 224 // without anyone trying to read it. 225 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); 226 if (fd == -1) { 227 if (errno == EEXIST) { 228 // The file exists, delete it and try again. 229 if (unlink(fname) == -1) { 230 // No point in retrying if the unlink failed. 231 ALOGE("error unlinking cache file %s: %s (%d)", fname, 232 strerror(errno), errno); 233 return; 234 } 235 // Retry now that we've unlinked the file. 236 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); 237 } 238 if (fd == -1) { 239 ALOGE("error creating cache file %s: %s (%d)", fname, 240 strerror(errno), errno); 241 return; 242 } 243 } 244 245 size_t fileSize = headerSize + cacheSize; 246 247 uint8_t* buf = new uint8_t [fileSize]; 248 if (!buf) { 249 ALOGE("error allocating buffer for cache contents: %s (%d)", 250 strerror(errno), errno); 251 close(fd); 252 unlink(fname); 253 return; 254 } 255 256 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL, 257 0); 258 if (err != OK) { 259 ALOGE("error writing cache contents: %s (%d)", strerror(-err), 260 -err); 261 delete [] buf; 262 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 272 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; 282 fchmod(fd, S_IRUSR); 283 close(fd); 284 } 285} 286 287void egl_cache_t::loadBlobCacheLocked() { 288 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) { 294 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(), 295 strerror(errno), errno); 296 } 297 return; 298 } 299 300 struct stat statBuf; 301 if (fstat(fd, &statBuf) == -1) { 302 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno); 303 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) { 310 ALOGE("cache file is too large: %#llx", statBuf.st_size); 311 close(fd); 312 return; 313 } 314 315 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, 316 PROT_READ, MAP_PRIVATE, fd, 0)); 317 if (buf == MAP_FAILED) { 318 ALOGE("error mmaping cache file: %s (%d)", strerror(errno), 319 errno); 320 close(fd); 321 return; 322 } 323 324 // Check the file magic and CRC 325 size_t cacheSize = fileSize - headerSize; 326 if (memcmp(buf, cacheFileMagic, 4) != 0) { 327 ALOGE("cache file has bad mojo"); 328 close(fd); 329 return; 330 } 331 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); 332 if (crc32c(buf + headerSize, cacheSize) != *crc) { 333 ALOGE("cache file failed CRC check"); 334 close(fd); 335 return; 336 } 337 338 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL, 339 0); 340 if (err != OK) { 341 ALOGE("error reading cache contents: %s (%d)", strerror(-err), 342 -err); 343 munmap(buf, fileSize); 344 close(fd); 345 return; 346 } 347 348 munmap(buf, fileSize); 349 close(fd); 350 } 351} 352 353// ---------------------------------------------------------------------------- 354}; // namespace android 355// ---------------------------------------------------------------------------- 356