EGL BlobCache: Support multifile cache and flexible size limit
This CL introduces the ability to support EGL blob cache using
multiple files. This allows us to increase the amount of cache
available to applications, without increasing process memory
used to get/set from the cache.
In order to support this, entries will be written to a new
directory in the same location:
$ adb shell
# cd /data/user_de/0/<packge_name>/code_cache
# ls -la com.android.opengl.shaders_cache.multifile
total 53M
drwx--S--- 2 u0_a276 u0_a276_cache 248K 2022-12-13 15:45 .
drwxrws--x 3 u0_a276 u0_a276_cache 404K 2022-12-13 11:26 ..
-rw------- 1 u0_a276 u0_a276_cache 7.1K 2022-12-13 15:43 1000572839
-rw------- 1 u0_a276 u0_a276_cache 8.9K 2022-12-13 15:43 1000616115
-rw------- 1 u0_a276 u0_a276_cache 5.0K 2022-12-13 15:43 1001816402
-rw------- 1 u0_a276 u0_a276_cache 4.3K 2022-12-13 15:44 1002265221
-rw------- 1 u0_a276 u0_a276_cache 8.2K 2022-12-13 15:44 1002773033
-rw------- 1 u0_a276 u0_a276_cache 2.8K 2022-12-13 15:45 1004532460
-rw------- 1 u0_a276 u0_a276_cache 4.3K 2022-12-13 15:45 1005120329
...
The filenames are generated by hashing the incoming key.
The cache limit is set in ag/20506291 by GraphicsEnvironment based on
getCacheQuotaBytes from StorageManager. If exceeded, we invoke an LRU
that clears files, oldest first, until under the cap.
The new mode is enable by default, but can be disabled with:
adb shell setprop debug.egl.blobcache.multifilemode false
The cache limit can also be modified with debug properties:
adb shell setprop debug.egl.blobcache.bytelimit <bytes>
Test: Multiple apps and ANGLE traces
Test: /data/nativetest64/EGL_test/EGL_test
Bug: b/246966894
Change-Id: I5e946d43728fdcea7dad08a4283129490893a122
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 8348d6c..5e729ef 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -16,6 +16,8 @@
#include "egl_cache.h"
+#include <android-base/properties.h>
+#include <inttypes.h>
#include <log/log.h>
#include <private/EGL/cache.h>
#include <unistd.h>
@@ -23,6 +25,7 @@
#include <thread>
#include "../egl_impl.h"
+#include "egl_cache_multifile.h"
#include "egl_display.h"
// Cache size limits.
@@ -33,6 +36,9 @@
// The time in seconds to wait before saving newly inserted cache entries.
static const unsigned int deferredSaveDelay = 4;
+// Delay before cleaning up multifile cache entries
+static const unsigned int deferredMultifileCleanupDelaySeconds = 1;
+
namespace android {
#define BC_EXT_STR "EGL_ANDROID_blob_cache"
@@ -58,7 +64,8 @@
//
// egl_cache_t definition
//
-egl_cache_t::egl_cache_t() : mInitialized(false) {}
+egl_cache_t::egl_cache_t()
+ : mInitialized(false), mMultifileMode(true), mCacheByteLimit(maxTotalSize) {}
egl_cache_t::~egl_cache_t() {}
@@ -101,6 +108,14 @@
}
}
+ mMultifileMode = true;
+
+ // Allow forcing monolithic cache for debug purposes
+ if (base::GetProperty("debug.egl.blobcache.multifilemode", "") == "false") {
+ ALOGD("Forcing monolithic cache due to debug.egl.blobcache.multifilemode == \"false\"");
+ mMultifileMode = false;
+ }
+
mInitialized = true;
}
@@ -110,6 +125,11 @@
mBlobCache->writeToFile();
}
mBlobCache = nullptr;
+ if (mMultifileMode) {
+ checkMultifileCacheSize(mCacheByteLimit);
+ }
+ mMultifileMode = false;
+ mInitialized = false;
}
void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
@@ -122,20 +142,37 @@
}
if (mInitialized) {
- BlobCache* bc = getBlobCacheLocked();
- bc->set(key, keySize, value, valueSize);
+ if (mMultifileMode) {
+ setBlobMultifile(key, keySize, value, valueSize, mFilename);
- if (!mSavePending) {
- mSavePending = true;
- std::thread deferredSaveThread([this]() {
- sleep(deferredSaveDelay);
- std::lock_guard<std::mutex> lock(mMutex);
- if (mInitialized && mBlobCache) {
- mBlobCache->writeToFile();
- }
- mSavePending = false;
- });
- deferredSaveThread.detach();
+ if (!mMultifileCleanupPending) {
+ mMultifileCleanupPending = true;
+ // Kick off a thread to cull cache files below limit
+ std::thread deferredMultifileCleanupThread([this]() {
+ sleep(deferredMultifileCleanupDelaySeconds);
+ std::lock_guard<std::mutex> lock(mMutex);
+ // Check the size of cache and remove entries to stay under limit
+ checkMultifileCacheSize(mCacheByteLimit);
+ mMultifileCleanupPending = false;
+ });
+ deferredMultifileCleanupThread.detach();
+ }
+ } else {
+ BlobCache* bc = getBlobCacheLocked();
+ bc->set(key, keySize, value, valueSize);
+
+ if (!mSavePending) {
+ mSavePending = true;
+ std::thread deferredSaveThread([this]() {
+ sleep(deferredSaveDelay);
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mInitialized && mBlobCache) {
+ mBlobCache->writeToFile();
+ }
+ mSavePending = false;
+ });
+ deferredSaveThread.detach();
+ }
}
}
}
@@ -145,13 +182,17 @@
std::lock_guard<std::mutex> lock(mMutex);
if (keySize < 0 || valueSize < 0) {
- ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
+ ALOGW("EGL_ANDROID_blob_cache get: negative sizes are not allowed");
return 0;
}
if (mInitialized) {
- BlobCache* bc = getBlobCacheLocked();
- return bc->get(key, keySize, value, valueSize);
+ if (mMultifileMode) {
+ return getBlobMultifile(key, keySize, value, valueSize, mFilename);
+ } else {
+ BlobCache* bc = getBlobCacheLocked();
+ return bc->get(key, keySize, value, valueSize);
+ }
}
return 0;
}
@@ -161,9 +202,34 @@
mFilename = filename;
}
+void egl_cache_t::setCacheLimit(int64_t cacheByteLimit) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mMultifileMode) {
+ // If we're not in multifile mode, ensure the cache limit is only being lowered,
+ // not increasing above the hard coded platform limit
+ if (cacheByteLimit > maxTotalSize) {
+ return;
+ }
+ }
+
+ mCacheByteLimit = cacheByteLimit;
+}
+
+size_t egl_cache_t::getCacheSize() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mMultifileMode) {
+ return getMultifileCacheSize();
+ }
+ if (mBlobCache) {
+ return mBlobCache->getSize();
+ }
+ return 0;
+}
+
BlobCache* egl_cache_t::getBlobCacheLocked() {
if (mBlobCache == nullptr) {
- mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
+ mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, mCacheByteLimit, mFilename));
}
return mBlobCache.get();
}