Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63a49a63 authored by Jamie Gennis's avatar Jamie Gennis Committed by Android (Google) Code Review
Browse files

Merge changes I18e5e789,I5cbaae2d into ics-mr1

* changes:
  EGL: implement loading and saving the cache
  EGL: use an in-memory the blob cache
parents 0fe35d09 98c63835
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -256,6 +256,21 @@ typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC)(void);
typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMENVPROC)(void);
#endif


/* EGL_ANDROID_blob_cache
 */
#ifndef EGL_ANDROID_blob_cache
#define EGL_ANDROID_blob_cache 1
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);
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI void EGLAPIENTRY eglSetBlobCacheFuncs(EGLDisplay dpy, EGLSetBlobFunc set, EGLGetBlobFunc get);
#endif /* EGL_EGLEXT_PROTOTYPES */
typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy,
        EGLSetBlobFunc set, EGLGetBlobFunc get);
#endif

#ifdef __cplusplus
}
#endif
+238 −18
Original line number Diff line number Diff line
@@ -19,6 +19,21 @@
#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;

// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -26,37 +41,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 +94,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 +104,210 @@ 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);
    }
}

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);
    }
}

// ----------------------------------------------------------------------------
+91 −1
Original line number Diff line number Diff line
@@ -14,20 +14,110 @@
 ** 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;

    // 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
+1 −0
Original line number Diff line number Diff line
@@ -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) {
+1 −2
Original line number Diff line number Diff line
@@ -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
Loading