1919#include " egl_impl.h"
2020#include " egldefs.h"
2121
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.
29+ static const size_t maxKeySize = 1024 ;
30+ static const size_t maxValueSize = 4096 ;
31+ static const size_t maxTotalSize = 64 * 1024 ;
32+
33+ // Cache file header
34+ static const char * cacheFileMagic = " EGL$" ;
35+ static const size_t cacheFileHeaderSize = 8 ;
36+
2237// ----------------------------------------------------------------------------
2338namespace android {
2439// ----------------------------------------------------------------------------
2540
2641#define BC_EXT_STR " EGL_ANDROID_blob_cache"
2742
2843//
29- // EGL_ANDROID_blob_cache types and functions
44+ // Callback functions passed to EGL.
3045//
31- typedef khronos_ssize_t EGLsizei;
32-
33- typedef void (*EGLSetBlobFunc) (const void * key, EGLsizei keySize,
34- const void * value, EGLsizei valueSize);
35-
36- typedef EGLsizei (*EGLGetBlobFunc) (const void * key, EGLsizei keySize,
37- void * value, EGLsizei valueSize);
46+ static void setBlob (const void * key, EGLsizei keySize, const void * value,
47+ EGLsizei valueSize) {
48+ egl_cache_t::get ()->setBlob (key, keySize, value, valueSize);
49+ }
3850
39- typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy,
40- EGLSetBlobFunc set, EGLGetBlobFunc get);
51+ static EGLsizei getBlob (const void * key, EGLsizei keySize, void * value,
52+ EGLsizei valueSize) {
53+ return egl_cache_t::get ()->getBlob (key, keySize, value, valueSize);
54+ }
4155
4256//
4357// egl_cache_t definition
4458//
45- static void setBlob (const void * key, EGLsizei keySize, const void * value,
46- EGLsizei valueSize) {
59+ egl_cache_t::egl_cache_t () :
60+ mInitialized (false ),
61+ mBlobCache (NULL ) {
4762}
4863
49- static EGLsizei getBlob (const void * key, EGLsizei keySize, void * value,
50- EGLsizei valueSize) {
51- return 0 ;
64+ egl_cache_t ::~egl_cache_t () {
5265}
5366
67+ egl_cache_t egl_cache_t ::sCache ;
68+
5469egl_cache_t * egl_cache_t::get () {
55- static egl_cache_t theCache;
56- return &theCache;
70+ return &sCache ;
5771}
5872
5973void egl_cache_t::initialize (egl_display_t *display) {
74+ Mutex::Autolock lock (mMutex );
6075 for (int i = 0 ; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
6176 egl_connection_t * const cnx = &gEGLImpl [i];
6277 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0 ) {
@@ -79,7 +94,8 @@ void egl_cache_t::initialize(egl_display_t *display) {
7994 continue ;
8095 }
8196
82- eglSetBlobCacheFuncs (display->disp [i].dpy , setBlob, getBlob);
97+ eglSetBlobCacheFuncs (display->disp [i].dpy , android::setBlob,
98+ android::getBlob);
8399 EGLint err = cnx->egl .eglGetError ();
84100 if (err != EGL_SUCCESS) {
85101 LOGE (" eglSetBlobCacheFuncs resulted in an error: %#x" ,
@@ -88,6 +104,210 @@ void egl_cache_t::initialize(egl_display_t *display) {
88104 }
89105 }
90106 }
107+ mInitialized = true ;
108+ }
109+
110+ void egl_cache_t::terminate () {
111+ Mutex::Autolock lock (mMutex );
112+ if (mBlobCache != NULL ) {
113+ saveBlobCacheLocked ();
114+ mBlobCache = NULL ;
115+ }
116+ mInitialized = false ;
117+ }
118+
119+ void egl_cache_t::setBlob (const void * key, EGLsizei keySize, const void * value,
120+ EGLsizei valueSize) {
121+ Mutex::Autolock lock (mMutex );
122+
123+ if (keySize < 0 || valueSize < 0 ) {
124+ LOGW (" EGL_ANDROID_blob_cache set: negative sizes are not allowed" );
125+ return ;
126+ }
127+
128+ if (mInitialized ) {
129+ sp<BlobCache> bc = getBlobCacheLocked ();
130+ bc->set (key, keySize, value, valueSize);
131+ }
132+ }
133+
134+ EGLsizei egl_cache_t::getBlob (const void * key, EGLsizei keySize, void * value,
135+ EGLsizei valueSize) {
136+ Mutex::Autolock lock (mMutex );
137+
138+ if (keySize < 0 || valueSize < 0 ) {
139+ LOGW (" EGL_ANDROID_blob_cache set: negative sizes are not allowed" );
140+ return 0 ;
141+ }
142+
143+ if (mInitialized ) {
144+ sp<BlobCache> bc = getBlobCacheLocked ();
145+ return bc->get (key, keySize, value, valueSize);
146+ }
147+ return 0 ;
148+ }
149+
150+ void egl_cache_t::setCacheFilename (const char * filename) {
151+ Mutex::Autolock lock (mMutex );
152+ mFilename = filename;
153+ }
154+
155+ sp<BlobCache> egl_cache_t::getBlobCacheLocked () {
156+ if (mBlobCache == NULL ) {
157+ mBlobCache = new BlobCache (maxKeySize, maxValueSize, maxTotalSize);
158+ loadBlobCacheLocked ();
159+ }
160+ return mBlobCache ;
161+ }
162+
163+ static uint32_t crc32c (const uint8_t * buf, size_t len) {
164+ const uint32_t polyBits = 0x82F63B78 ;
165+ uint32_t r = 0 ;
166+ for (size_t i = 0 ; i < len; i++) {
167+ r ^= buf[i];
168+ for (int j = 0 ; j < 8 ; j++) {
169+ if (r & 1 ) {
170+ r = (r >> 1 ) ^ polyBits;
171+ } else {
172+ r >>= 1 ;
173+ }
174+ }
175+ }
176+ return r;
177+ }
178+
179+ void egl_cache_t::saveBlobCacheLocked () {
180+ if (mFilename .length () > 0 ) {
181+ size_t cacheSize = mBlobCache ->getFlattenedSize ();
182+ size_t headerSize = cacheFileHeaderSize;
183+ const char * fname = mFilename .string ();
184+
185+ // Try to create the file with no permissions so we can write it
186+ // without anyone trying to read it.
187+ int fd = open (fname, O_CREAT | O_EXCL | O_RDWR, 0 );
188+ if (fd == -1 ) {
189+ if (errno == EEXIST) {
190+ // The file exists, delete it and try again.
191+ if (unlink (fname) == -1 ) {
192+ // No point in retrying if the unlink failed.
193+ LOGE (" error unlinking cache file %s: %s (%d)" , fname,
194+ strerror (errno), errno);
195+ return ;
196+ }
197+ // Retry now that we've unlinked the file.
198+ fd = open (fname, O_CREAT | O_EXCL | O_RDWR, 0 );
199+ }
200+ if (fd == -1 ) {
201+ LOGE (" error creating cache file %s: %s (%d)" , fname,
202+ strerror (errno), errno);
203+ return ;
204+ }
205+ }
206+
207+ size_t fileSize = headerSize + cacheSize;
208+ if (ftruncate (fd, fileSize) == -1 ) {
209+ LOGE (" error setting cache file size: %s (%d)" , strerror (errno),
210+ errno);
211+ close (fd);
212+ unlink (fname);
213+ return ;
214+ }
215+
216+ uint8_t * buf = reinterpret_cast <uint8_t *>(mmap (NULL , fileSize,
217+ PROT_WRITE, MAP_SHARED, fd, 0 ));
218+ if (buf == MAP_FAILED) {
219+ LOGE (" error mmaping cache file: %s (%d)" , strerror (errno),
220+ errno);
221+ close (fd);
222+ unlink (fname);
223+ return ;
224+ }
225+
226+ status_t err = mBlobCache ->flatten (buf + headerSize, cacheSize, NULL ,
227+ 0 );
228+ if (err != OK) {
229+ LOGE (" error writing cache contents: %s (%d)" , strerror (-err),
230+ -err);
231+ munmap (buf, fileSize);
232+ close (fd);
233+ unlink (fname);
234+ return ;
235+ }
236+
237+ // Write the file magic and CRC
238+ memcpy (buf, cacheFileMagic, 4 );
239+ uint32_t * crc = reinterpret_cast <uint32_t *>(buf + 4 );
240+ *crc = crc32c (buf + headerSize, cacheSize);
241+
242+ munmap (buf, fileSize);
243+ fchmod (fd, S_IRUSR);
244+ close (fd);
245+ }
246+ }
247+
248+ void egl_cache_t::loadBlobCacheLocked () {
249+ if (mFilename .length () > 0 ) {
250+ size_t headerSize = cacheFileHeaderSize;
251+
252+ int fd = open (mFilename .string (), O_RDONLY, 0 );
253+ if (fd == -1 ) {
254+ if (errno != ENOENT) {
255+ LOGE (" error opening cache file %s: %s (%d)" , mFilename .string (),
256+ strerror (errno), errno);
257+ }
258+ return ;
259+ }
260+
261+ struct stat statBuf;
262+ if (fstat (fd, &statBuf) == -1 ) {
263+ LOGE (" error stat'ing cache file: %s (%d)" , strerror (errno), errno);
264+ close (fd);
265+ return ;
266+ }
267+
268+ // Sanity check the size before trying to mmap it.
269+ size_t fileSize = statBuf.st_size ;
270+ if (fileSize > maxTotalSize * 2 ) {
271+ LOGE (" cache file is too large: %#llx" , statBuf.st_size );
272+ close (fd);
273+ return ;
274+ }
275+
276+ uint8_t * buf = reinterpret_cast <uint8_t *>(mmap (NULL , fileSize,
277+ PROT_READ, MAP_PRIVATE, fd, 0 ));
278+ if (buf == MAP_FAILED) {
279+ LOGE (" error mmaping cache file: %s (%d)" , strerror (errno),
280+ errno);
281+ close (fd);
282+ return ;
283+ }
284+
285+ // Check the file magic and CRC
286+ size_t cacheSize = fileSize - headerSize;
287+ if (memcmp (buf, cacheFileMagic, 4 ) != 0 ) {
288+ LOGE (" cache file has bad mojo" );
289+ close (fd);
290+ return ;
291+ }
292+ uint32_t * crc = reinterpret_cast <uint32_t *>(buf + 4 );
293+ if (crc32c (buf + headerSize, cacheSize) != *crc) {
294+ LOGE (" cache file failed CRC check" );
295+ close (fd);
296+ return ;
297+ }
298+
299+ status_t err = mBlobCache ->unflatten (buf + headerSize, cacheSize, NULL , 0 );
300+ if (err != OK) {
301+ LOGE (" error reading cache contents: %s (%d)" , strerror (-err),
302+ -err);
303+ munmap (buf, fileSize);
304+ close (fd);
305+ return ;
306+ }
307+
308+ munmap (buf, fileSize);
309+ close (fd);
310+ }
91311}
92312
93313// ----------------------------------------------------------------------------
0 commit comments