egl_cache.cpp revision 39c24a20bbc697630d2b92c251b70c04d6f9d00c
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 if (mBlobCache != NULL) { 130 saveBlobCacheLocked(); 131 mBlobCache = NULL; 132 } 133 mInitialized = false; 134} 135 136void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, 137 const void* value, EGLsizeiANDROID valueSize) { 138 Mutex::Autolock lock(mMutex); 139 140 if (keySize < 0 || valueSize < 0) { 141 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); 142 return; 143 } 144 145 if (mInitialized) { 146 sp<BlobCache> bc = getBlobCacheLocked(); 147 bc->set(key, keySize, value, valueSize); 148 149 if (!mSavePending) { 150 class DeferredSaveThread : public Thread { 151 public: 152 DeferredSaveThread() : Thread(false) {} 153 154 virtual bool threadLoop() { 155 sleep(deferredSaveDelay); 156 egl_cache_t* c = egl_cache_t::get(); 157 Mutex::Autolock lock(c->mMutex); 158 if (c->mInitialized) { 159 c->saveBlobCacheLocked(); 160 } 161 c->mSavePending = false; 162 return false; 163 } 164 }; 165 166 // The thread will hold a strong ref to itself until it has finished 167 // running, so there's no need to keep a ref around. 168 sp<Thread> deferredSaveThread(new DeferredSaveThread()); 169 mSavePending = true; 170 deferredSaveThread->run(); 171 } 172 } 173} 174 175EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, 176 void* value, EGLsizeiANDROID valueSize) { 177 Mutex::Autolock lock(mMutex); 178 179 if (keySize < 0 || valueSize < 0) { 180 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); 181 return 0; 182 } 183 184 if (mInitialized) { 185 sp<BlobCache> bc = getBlobCacheLocked(); 186 return bc->get(key, keySize, value, valueSize); 187 } 188 return 0; 189} 190 191void egl_cache_t::setCacheFilename(const char* filename) { 192 Mutex::Autolock lock(mMutex); 193 mFilename = filename; 194} 195 196sp<BlobCache> egl_cache_t::getBlobCacheLocked() { 197 if (mBlobCache == NULL) { 198 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize); 199 loadBlobCacheLocked(); 200 } 201 return mBlobCache; 202} 203 204static uint32_t crc32c(const uint8_t* buf, size_t len) { 205 const uint32_t polyBits = 0x82F63B78; 206 uint32_t r = 0; 207 for (size_t i = 0; i < len; i++) { 208 r ^= buf[i]; 209 for (int j = 0; j < 8; j++) { 210 if (r & 1) { 211 r = (r >> 1) ^ polyBits; 212 } else { 213 r >>= 1; 214 } 215 } 216 } 217 return r; 218} 219 220void egl_cache_t::saveBlobCacheLocked() { 221 if (mFilename.length() > 0) { 222 size_t cacheSize = mBlobCache->getFlattenedSize(); 223 size_t headerSize = cacheFileHeaderSize; 224 const char* fname = mFilename.string(); 225 226 // Try to create the file with no permissions so we can write it 227 // without anyone trying to read it. 228 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); 229 if (fd == -1) { 230 if (errno == EEXIST) { 231 // The file exists, delete it and try again. 232 if (unlink(fname) == -1) { 233 // No point in retrying if the unlink failed. 234 ALOGE("error unlinking cache file %s: %s (%d)", fname, 235 strerror(errno), errno); 236 return; 237 } 238 // Retry now that we've unlinked the file. 239 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); 240 } 241 if (fd == -1) { 242 ALOGE("error creating cache file %s: %s (%d)", fname, 243 strerror(errno), errno); 244 return; 245 } 246 } 247 248 size_t fileSize = headerSize + cacheSize; 249 250 uint8_t* buf = new uint8_t [fileSize]; 251 if (!buf) { 252 ALOGE("error allocating buffer for cache contents: %s (%d)", 253 strerror(errno), errno); 254 close(fd); 255 unlink(fname); 256 return; 257 } 258 259 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL, 260 0); 261 if (err != OK) { 262 ALOGE("error writing cache contents: %s (%d)", strerror(-err), 263 -err); 264 delete [] buf; 265 close(fd); 266 unlink(fname); 267 return; 268 } 269 270 // Write the file magic and CRC 271 memcpy(buf, cacheFileMagic, 4); 272 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); 273 *crc = crc32c(buf + headerSize, cacheSize); 274 275 if (write(fd, buf, fileSize) == -1) { 276 ALOGE("error writing cache file: %s (%d)", strerror(errno), 277 errno); 278 delete [] buf; 279 close(fd); 280 unlink(fname); 281 return; 282 } 283 284 delete [] buf; 285 fchmod(fd, S_IRUSR); 286 close(fd); 287 } 288} 289 290void egl_cache_t::loadBlobCacheLocked() { 291 if (mFilename.length() > 0) { 292 size_t headerSize = cacheFileHeaderSize; 293 294 int fd = open(mFilename.string(), O_RDONLY, 0); 295 if (fd == -1) { 296 if (errno != ENOENT) { 297 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(), 298 strerror(errno), errno); 299 } 300 return; 301 } 302 303 struct stat statBuf; 304 if (fstat(fd, &statBuf) == -1) { 305 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno); 306 close(fd); 307 return; 308 } 309 310 // Sanity check the size before trying to mmap it. 311 size_t fileSize = statBuf.st_size; 312 if (fileSize > maxTotalSize * 2) { 313 ALOGE("cache file is too large: %#llx", statBuf.st_size); 314 close(fd); 315 return; 316 } 317 318 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, 319 PROT_READ, MAP_PRIVATE, fd, 0)); 320 if (buf == MAP_FAILED) { 321 ALOGE("error mmaping cache file: %s (%d)", strerror(errno), 322 errno); 323 close(fd); 324 return; 325 } 326 327 // Check the file magic and CRC 328 size_t cacheSize = fileSize - headerSize; 329 if (memcmp(buf, cacheFileMagic, 4) != 0) { 330 ALOGE("cache file has bad mojo"); 331 close(fd); 332 return; 333 } 334 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); 335 if (crc32c(buf + headerSize, cacheSize) != *crc) { 336 ALOGE("cache file failed CRC check"); 337 close(fd); 338 return; 339 } 340 341 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL, 342 0); 343 if (err != OK) { 344 ALOGE("error reading cache contents: %s (%d)", strerror(-err), 345 -err); 346 munmap(buf, fileSize); 347 close(fd); 348 return; 349 } 350 351 munmap(buf, fileSize); 352 close(fd); 353 } 354} 355 356// ---------------------------------------------------------------------------- 357}; // namespace android 358// ---------------------------------------------------------------------------- 359