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