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