diff options
Diffstat (limited to 'opengl/libs')
| -rw-r--r-- | opengl/libs/EGL/egl_cache.cpp | 283 | ||||
| -rw-r--r-- | opengl/libs/EGL/egl_cache.h | 99 | ||||
| -rw-r--r-- | opengl/libs/EGL/egl_display.cpp | 1 | ||||
| -rw-r--r-- | opengl/libs/EGL/egl_display.h | 3 |
4 files changed, 365 insertions, 21 deletions
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 1e64302..522421b 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -19,6 +19,24 @@ #include "egl_impl.h" #include "egldefs.h" +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +// Cache size limits. +static const size_t maxKeySize = 1024; +static const size_t maxValueSize = 4096; +static const size_t maxTotalSize = 64 * 1024; + +// Cache file header +static const char* cacheFileMagic = "EGL$"; +static const size_t cacheFileHeaderSize = 8; + +// The time in seconds to wait before saving newly inserted cache entries. +static const unsigned int deferredSaveDelay = 4; + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -26,37 +44,37 @@ namespace android { #define BC_EXT_STR "EGL_ANDROID_blob_cache" // -// EGL_ANDROID_blob_cache types and functions +// Callback functions passed to EGL. // -typedef khronos_ssize_t EGLsizei; - -typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize, - const void* value, EGLsizei valueSize); - -typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize, - void* value, EGLsizei valueSize); +static void setBlob(const void* key, EGLsizei keySize, const void* value, + EGLsizei valueSize) { + egl_cache_t::get()->setBlob(key, keySize, value, valueSize); +} -typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy, - EGLSetBlobFunc set, EGLGetBlobFunc get); +static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value, + EGLsizei valueSize) { + return egl_cache_t::get()->getBlob(key, keySize, value, valueSize); +} // // egl_cache_t definition // -static void setBlob(const void* key, EGLsizei keySize, const void* value, - EGLsizei valueSize) { +egl_cache_t::egl_cache_t() : + mInitialized(false), + mBlobCache(NULL) { } -static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value, - EGLsizei valueSize) { - return 0; +egl_cache_t::~egl_cache_t() { } +egl_cache_t egl_cache_t::sCache; + egl_cache_t* egl_cache_t::get() { - static egl_cache_t theCache; - return &theCache; + return &sCache; } void egl_cache_t::initialize(egl_display_t *display) { + Mutex::Autolock lock(mMutex); for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { egl_connection_t* const cnx = &gEGLImpl[i]; if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { @@ -79,7 +97,8 @@ void egl_cache_t::initialize(egl_display_t *display) { continue; } - eglSetBlobCacheFuncs(display->disp[i].dpy, setBlob, getBlob); + eglSetBlobCacheFuncs(display->disp[i].dpy, android::setBlob, + android::getBlob); EGLint err = cnx->egl.eglGetError(); if (err != EGL_SUCCESS) { LOGE("eglSetBlobCacheFuncs resulted in an error: %#x", @@ -88,6 +107,234 @@ void egl_cache_t::initialize(egl_display_t *display) { } } } + mInitialized = true; +} + +void egl_cache_t::terminate() { + Mutex::Autolock lock(mMutex); + if (mBlobCache != NULL) { + saveBlobCacheLocked(); + mBlobCache = NULL; + } + mInitialized = false; +} + +void egl_cache_t::setBlob(const void* key, EGLsizei keySize, const void* value, + EGLsizei valueSize) { + Mutex::Autolock lock(mMutex); + + if (keySize < 0 || valueSize < 0) { + LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); + return; + } + + if (mInitialized) { + sp<BlobCache> bc = getBlobCacheLocked(); + bc->set(key, keySize, value, valueSize); + + if (!mSavePending) { + class DeferredSaveThread : public Thread { + public: + DeferredSaveThread() : Thread(false) {} + + virtual bool threadLoop() { + sleep(deferredSaveDelay); + egl_cache_t* c = egl_cache_t::get(); + Mutex::Autolock lock(c->mMutex); + if (c->mInitialized) { + c->saveBlobCacheLocked(); + } + c->mSavePending = false; + return false; + } + }; + + // The thread will hold a strong ref to itself until it has finished + // running, so there's no need to keep a ref around. + sp<Thread> deferredSaveThread(new DeferredSaveThread()); + mSavePending = true; + deferredSaveThread->run(); + } + } +} + +EGLsizei egl_cache_t::getBlob(const void* key, EGLsizei keySize, void* value, + EGLsizei valueSize) { + Mutex::Autolock lock(mMutex); + + if (keySize < 0 || valueSize < 0) { + LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); + return 0; + } + + if (mInitialized) { + sp<BlobCache> bc = getBlobCacheLocked(); + return bc->get(key, keySize, value, valueSize); + } + return 0; +} + +void egl_cache_t::setCacheFilename(const char* filename) { + Mutex::Autolock lock(mMutex); + mFilename = filename; +} + +sp<BlobCache> egl_cache_t::getBlobCacheLocked() { + if (mBlobCache == NULL) { + mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize); + loadBlobCacheLocked(); + } + return mBlobCache; +} + +static uint32_t crc32c(const uint8_t* buf, size_t len) { + const uint32_t polyBits = 0x82F63B78; + uint32_t r = 0; + for (size_t i = 0; i < len; i++) { + r ^= buf[i]; + for (int j = 0; j < 8; j++) { + if (r & 1) { + r = (r >> 1) ^ polyBits; + } else { + r >>= 1; + } + } + } + return r; +} + +void egl_cache_t::saveBlobCacheLocked() { + if (mFilename.length() > 0) { + size_t cacheSize = mBlobCache->getFlattenedSize(); + size_t headerSize = cacheFileHeaderSize; + const char* fname = mFilename.string(); + + // Try to create the file with no permissions so we can write it + // without anyone trying to read it. + int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); + if (fd == -1) { + if (errno == EEXIST) { + // The file exists, delete it and try again. + if (unlink(fname) == -1) { + // No point in retrying if the unlink failed. + LOGE("error unlinking cache file %s: %s (%d)", fname, + strerror(errno), errno); + return; + } + // Retry now that we've unlinked the file. + fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); + } + if (fd == -1) { + LOGE("error creating cache file %s: %s (%d)", fname, + strerror(errno), errno); + return; + } + } + + size_t fileSize = headerSize + cacheSize; + if (ftruncate(fd, fileSize) == -1) { + LOGE("error setting cache file size: %s (%d)", strerror(errno), + errno); + close(fd); + unlink(fname); + return; + } + + uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, + PROT_WRITE, MAP_SHARED, fd, 0)); + if (buf == MAP_FAILED) { + LOGE("error mmaping cache file: %s (%d)", strerror(errno), + errno); + close(fd); + unlink(fname); + return; + } + + status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL, + 0); + if (err != OK) { + LOGE("error writing cache contents: %s (%d)", strerror(-err), + -err); + munmap(buf, fileSize); + close(fd); + unlink(fname); + return; + } + + // Write the file magic and CRC + memcpy(buf, cacheFileMagic, 4); + uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); + *crc = crc32c(buf + headerSize, cacheSize); + + munmap(buf, fileSize); + fchmod(fd, S_IRUSR); + close(fd); + } +} + +void egl_cache_t::loadBlobCacheLocked() { + if (mFilename.length() > 0) { + size_t headerSize = cacheFileHeaderSize; + + int fd = open(mFilename.string(), O_RDONLY, 0); + if (fd == -1) { + if (errno != ENOENT) { + LOGE("error opening cache file %s: %s (%d)", mFilename.string(), + strerror(errno), errno); + } + return; + } + + struct stat statBuf; + if (fstat(fd, &statBuf) == -1) { + LOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno); + close(fd); + return; + } + + // Sanity check the size before trying to mmap it. + size_t fileSize = statBuf.st_size; + if (fileSize > maxTotalSize * 2) { + LOGE("cache file is too large: %#llx", statBuf.st_size); + close(fd); + return; + } + + uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, + PROT_READ, MAP_PRIVATE, fd, 0)); + if (buf == MAP_FAILED) { + LOGE("error mmaping cache file: %s (%d)", strerror(errno), + errno); + close(fd); + return; + } + + // Check the file magic and CRC + size_t cacheSize = fileSize - headerSize; + if (memcmp(buf, cacheFileMagic, 4) != 0) { + LOGE("cache file has bad mojo"); + close(fd); + return; + } + uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); + if (crc32c(buf + headerSize, cacheSize) != *crc) { + LOGE("cache file failed CRC check"); + close(fd); + return; + } + + status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL, 0); + if (err != OK) { + LOGE("error reading cache contents: %s (%d)", strerror(-err), + -err); + munmap(buf, fileSize); + close(fd); + return; + } + + munmap(buf, fileSize); + close(fd); + } } // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h index 1fcfacc..4389623 100644 --- a/opengl/libs/EGL/egl_cache.h +++ b/opengl/libs/EGL/egl_cache.h @@ -14,20 +14,117 @@ ** limitations under the License. */ +#ifndef ANDROID_EGL_CACHE_H +#define ANDROID_EGL_CACHE_H + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <utils/BlobCache.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- class egl_display_t; -class egl_cache_t { +class EGLAPI egl_cache_t { public: + // get returns a pointer to the singleton egl_cache_t object. This + // singleton object will never be destroyed. static egl_cache_t* get(); + // initialize puts the egl_cache_t into an initialized state, such that it + // is able to insert and retrieve entries from the cache. This should be + // called when EGL is initialized. When not in the initialized state the + // getBlob and setBlob methods will return without performing any cache + // operations. void initialize(egl_display_t* display); + + // terminate puts the egl_cache_t back into the uninitialized state. When + // in this state the getBlob and setBlob methods will return without + // performing any cache operations. + void terminate(); + + // setBlob attempts to insert a new key/value blob pair into the cache. + // This will be called by the hardware vendor's EGL implementation via the + // EGL_ANDROID_blob_cache extension. + void setBlob(const void* key, EGLsizei keySize, const void* value, + EGLsizei valueSize); + + // getBlob attempts to retrieve the value blob associated with a given key + // blob from cache. This will be called by the hardware vendor's EGL + // implementation via the EGL_ANDROID_blob_cache extension. + EGLsizei getBlob(const void* key, EGLsizei keySize, void* value, + EGLsizei valueSize); + + // setCacheFilename sets the name of the file that should be used to store + // cache contents from one program invocation to another. + void setCacheFilename(const char* filename); + +private: + // Creation and (the lack of) destruction is handled internally. + egl_cache_t(); + ~egl_cache_t(); + + // Copying is disallowed. + egl_cache_t(const egl_cache_t&); // not implemented + void operator=(const egl_cache_t&); // not implemented + + // getBlobCacheLocked returns the BlobCache object being used to store the + // key/value blob pairs. If the BlobCache object has not yet been created, + // this will do so, loading the serialized cache contents from disk if + // possible. + sp<BlobCache> getBlobCacheLocked(); + + // saveBlobCache attempts to save the current contents of mBlobCache to + // disk. + void saveBlobCacheLocked(); + + // loadBlobCache attempts to load the saved cache contents from disk into + // mBlobCache. + void loadBlobCacheLocked(); + + // mInitialized indicates whether the egl_cache_t is in the initialized + // state. It is initialized to false at construction time, and gets set to + // true when initialize is called. It is set back to false when terminate + // is called. When in this state, the cache behaves as normal. When not, + // the getBlob and setBlob methods will return without performing any cache + // operations. + bool mInitialized; + + // mBlobCache is the cache in which the key/value blob pairs are stored. It + // is initially NULL, and will be initialized by getBlobCacheLocked the + // first time it's needed. + sp<BlobCache> mBlobCache; + + // mFilename is the name of the file for storing cache contents in between + // program invocations. It is initialized to an empty string at + // construction time, and can be set with the setCacheFilename method. An + // empty string indicates that the cache should not be saved to or restored + // from disk. + String8 mFilename; + + // mSavePending indicates whether or not a deferred save operation is + // pending. Each time a key/value pair is inserted into the cache via + // setBlob, a deferred save is initiated if one is not already pending. + // This will wait some amount of time and then trigger a save of the cache + // contents to disk. + bool mSavePending; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables. It must be locked whenever the member variables are accessed. + mutable Mutex mMutex; + + // sCache is the singleton egl_cache_t object. + static egl_cache_t sCache; }; // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- + +#endif // ANDROID_EGL_CACHE_H diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 0f92864..558ca77 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -44,6 +44,7 @@ egl_display_t::egl_display_t() : egl_display_t::~egl_display_t() { magic = 0; + egl_cache_t::get()->terminate(); } egl_display_t* egl_display_t::get(EGLDisplay dpy) { diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 113595f..1c1092c 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -59,7 +59,7 @@ struct egl_config_t { // ---------------------------------------------------------------------------- -class egl_display_t { +class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes static egl_display_t sDisplay[NUM_DISPLAYS]; EGLDisplay getDisplay(EGLNativeDisplayType display); @@ -141,4 +141,3 @@ EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface); // ---------------------------------------------------------------------------- #endif // ANDROID_EGL_DISPLAY_H - |
