1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "egl_blob_cache.h"
16 #include "wrapper_log.h"
17 #include <sys/stat.h>
18 #include <sys/mman.h>
19 #include <errors.h>
20 #include <cinttypes>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <thread>
24
25 namespace OHOS {
26 static const char* CACHE_MAGIC = "OSOH";
27 BlobCache* BlobCache::blobCache_;
28
Get()29 BlobCache* BlobCache::Get()
30 {
31 if (BlobCache::blobCache_ == nullptr) {
32 BlobCache::blobCache_ = new BlobCache();
33 }
34 return BlobCache::blobCache_;
35 }
36
Blob(const void * dataIn,size_t size)37 BlobCache::Blob::Blob(const void *dataIn, size_t size) :dataSize(size)
38 {
39 prev_ = nullptr;
40 next_ = nullptr;
41 data = nullptr;
42 if (size > 0) {
43 data = malloc(size);
44 }
45 if (data == nullptr) {
46 dataSize = 0;
47 }
48 if (data != nullptr) {
49 memcpy_s(data, size, const_cast<void *>(dataIn), size);
50 }
51 }
52
~Blob()53 BlobCache::Blob::~Blob()
54 {
55 free(data);
56 }
57
BlobCache()58 BlobCache::BlobCache()
59 {
60 head_ = std::make_shared<Blob>(nullptr, 0);
61 tail_ = std::make_shared<Blob>(nullptr, 0);
62 head_->next_ = tail_;
63 tail_->prev_ = head_;
64 blobSizeMax_ = MAX_SHADER;
65 maxShaderSize_ = MAX_SHADER_SIZE;
66 initStatus_ = false;
67 fileName_ = std::string("/blobShader");
68 saveStatus_ = false;
69 blobSize_ = 0;
70 }
71
~BlobCache()72 BlobCache::~BlobCache()
73 {
74 if (blobCache_) {
75 blobCache_ = nullptr;
76 }
77 }
78
Terminate()79 void BlobCache::Terminate()
80 {
81 if (blobCache_) {
82 blobCache_->WriteToDisk();
83 }
84 blobCache_ = nullptr;
85 }
86
GetMapSize() const87 int BlobCache::GetMapSize() const
88 {
89 if (blobCache_) {
90 return (int)mBlobMap_.size();
91 }
92 return 0;
93 }
94
Init(EglWrapperDisplay * display)95 void BlobCache::Init(EglWrapperDisplay* display)
96 {
97 if (!initStatus_) {
98 return;
99 }
100 EglWrapperDispatchTablePtr table = &gWrapperHook;
101 if (table->isLoad && table->egl.eglSetBlobCacheFuncsANDROID) {
102 table->egl.eglSetBlobCacheFuncsANDROID(display->GetEglDisplay(),
103 BlobCache::SetBlobFunc, BlobCache::GetBlobFunc);
104 } else {
105 WLOGE("eglSetBlobCacheFuncsANDROID not found.");
106 }
107 }
108
SetBlobFunc(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)109 void BlobCache::SetBlobFunc(const void* key, EGLsizeiANDROID keySize, const void* value,
110 EGLsizeiANDROID valueSize)
111 {
112 BlobCache::Get()->SetBlobLock(key, keySize, value, valueSize);
113 }
GetBlobFunc(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)114 EGLsizeiANDROID BlobCache::GetBlobFunc(const void *key, EGLsizeiANDROID keySize, void *value,
115 EGLsizeiANDROID valueSize)
116 {
117 return BlobCache::Get()->GetBlobLock(key, keySize, value, valueSize);
118 }
119
SetBlobLock(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)120 void BlobCache::SetBlobLock(const void* key, EGLsizeiANDROID keySize, const void* value,
121 EGLsizeiANDROID valueSize)
122 {
123 std::lock_guard<std::mutex> lock(blobmutex_);
124 SetBlob(key, keySize, value, valueSize);
125 }
GetBlobLock(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)126 EGLsizeiANDROID BlobCache::GetBlobLock(const void *key, EGLsizeiANDROID keySize, void *value,
127 EGLsizeiANDROID valueSize)
128 {
129 std::lock_guard<std::mutex> lock(blobmutex_);
130 return GetBlob(key, keySize, value, valueSize);
131 }
132
MoveToFront(std::shared_ptr<Blob> & cur)133 void BlobCache::MoveToFront(std::shared_ptr<Blob>& cur)
134 {
135 head_->next_->prev_ = cur;
136 cur->next_ = head_->next_;
137 head_->next_ = cur;
138 cur->prev_ = head_;
139 }
140
SetBlob(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)141 void BlobCache::SetBlob(const void *key, EGLsizeiANDROID keySize, const void *value,
142 EGLsizeiANDROID valueSize)
143 {
144 if (keySize <= 0 || valueSize <= 0 || key == nullptr || value == nullptr) {
145 return;
146 }
147 if (!readStatus_) {
148 ReadFromDisk();
149 }
150 std::shared_ptr<Blob> keyBlob = std::make_shared<Blob>(key, (size_t)keySize);
151 auto it = mBlobMap_.find(keyBlob);
152 if (it != mBlobMap_.end()) {
153 free(it->second->data);
154 it->second->data = malloc(valueSize);
155 if (it->second->data != nullptr) {
156 it->second->dataSize = valueSize;
157 } else {
158 it->second->dataSize = 0;
159 return;
160 }
161 memcpy_s(it->second->data, valueSize, value, valueSize);
162 return;
163 }
164 if (blobSize_ >= blobSizeMax_) {
165 int count = 0;
166 while (count <= MAX_SHADER_DELETE) {
167 std::shared_ptr<Blob> deleteblob = tail_->prev_;
168 deleteblob->prev_->next_ = tail_;
169 tail_->prev_ = deleteblob->prev_;
170 mBlobMap_.erase(deleteblob);
171 --blobSize_;
172 ++count;
173 }
174 }
175 std::shared_ptr<Blob> valueBlob = std::make_shared<Blob>(value, (size_t)valueSize);
176 mBlobMap_.emplace(keyBlob, valueBlob);
177 MoveToFront(keyBlob);
178 ++blobSize_;
179 if (!saveStatus_) {
180 saveStatus_ = true;
181 std::thread deferSaveThread([this]() {
182 sleep(DEFER_SAVE_MIN);
183 if (blobCache_) {
184 blobCache_->WriteToDisk();
185 }
186 saveStatus_ = false;
187 });
188 deferSaveThread.detach();
189 }
190 }
191
GetBlob(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)192 EGLsizeiANDROID BlobCache::GetBlob(const void *key, EGLsizeiANDROID keySize, void *value,
193 EGLsizeiANDROID valueSize)
194 {
195 EGLsizeiANDROID ret = 0;
196 if (keySize <= 0 || key == nullptr) {
197 return ret;
198 }
199 if (!readStatus_) {
200 ReadFromDisk();
201 }
202 std::shared_ptr<Blob> keyBlob = std::make_shared<Blob>(key, (size_t)keySize);
203 auto it = mBlobMap_.find(keyBlob);
204 if (it != mBlobMap_.end()) {
205 ret = static_cast<EGLsizeiANDROID>(it->second->dataSize);
206 if (valueSize < ret) {
207 WLOGD("valueSize not enough");
208 } else if (ret == 0) {
209 WLOGE("shader not exist");
210 } else {
211 errno_t status = memcpy_s(value, valueSize, it->second->data, it->second->dataSize);
212 if (status != EOK) {
213 WLOGE("memcpy_s failed");
214 return ret;
215 }
216 auto moveblob = it->first;
217 moveblob->prev_->next_ = moveblob->next_;
218 moveblob->next_->prev_ = moveblob->prev_;
219 MoveToFront(moveblob);
220 }
221 }
222
223 return ret;
224 }
225
SetCacheDir(const std::string dir)226 void BlobCache::SetCacheDir(const std::string dir)
227 {
228 struct stat dirStat;
229 if (stat(dir.c_str(), &dirStat) != 0) {
230 WLOGE("inputdir not Create");
231 initStatus_ = false;
232 return;
233 }
234
235 struct stat cachedirstat;
236 struct stat ddkdirstat;
237 cacheDir_ = dir + std::string("/blobShader");
238 ddkCacheDir_ = dir + std::string("/ddkShader");
239
240 if (stat(cacheDir_.c_str(), &cachedirstat) != 0) {
241 initStatus_ = false;
242 int ret = mkdir(cacheDir_.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH);
243 if (ret == -1) {
244 initStatus_ = false;
245 WLOGE("cacheDir_ not Create");
246 } else {
247 initStatus_ = true;
248 }
249 } else {
250 initStatus_ = true;
251 }
252
253 if (stat(ddkCacheDir_.c_str(), &ddkdirstat) != 0) {
254 int ret = mkdir(ddkCacheDir_.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH);
255 if (ret == -1 && errno != EEXIST) {
256 WLOGE("ddkCacheDir_ not Create");
257 }
258 }
259 }
260
SetCacheShaderSize(int shadermax)261 void BlobCache::SetCacheShaderSize(int shadermax)
262 {
263 if (shadermax <= 0 || shadermax > MAX_SHADER) {
264 return;
265 }
266 blobSizeMax_ = shadermax;
267 }
268
Formatfile(size_t size)269 static inline size_t Formatfile(size_t size)
270 {
271 return (size + FORMAT_OFFSET) & ~FORMAT_OFFSET;
272 }
273
GetCacheSize() const274 size_t BlobCache::GetCacheSize() const
275 {
276 size_t headSize = sizeof(CacheHeader);
277 size_t ret = 0;
278 for (auto item = mBlobMap_.begin(); item != mBlobMap_.end(); ++item) {
279 size_t innerSize = headSize + item->first->dataSize + item->second->dataSize;
280 ret += Formatfile(innerSize);
281 }
282 return ret;
283 }
284
WriteToDisk()285 void BlobCache::WriteToDisk()
286 {
287 std::lock_guard<std::mutex> lock(blobmutex_);
288 struct stat dirStat;
289 if (stat(cacheDir_.c_str(), &dirStat) != 0) {
290 return;
291 }
292 std::string storefile = cacheDir_ + fileName_;
293 int fd = open(storefile.c_str(), O_CREAT | O_EXCL | O_RDWR, 0);
294 if (fd == -1) {
295 if (errno == EEXIST) {
296 if (unlink(storefile.c_str()) == -1) {
297 return;
298 }
299 fd = open(storefile.c_str(), O_CREAT | O_EXCL | O_RDWR, 0);
300 }
301 if (fd == -1) {
302 return;
303 }
304 }
305 size_t filesize = GetCacheSize();
306 size_t headsize = sizeof(CacheHeader);
307 size_t bufsize = filesize + CACHE_HEAD;
308 uint8_t *buf = new uint8_t[bufsize];
309 size_t offset = CACHE_HEAD;
310 for (auto item = mBlobMap_.begin(); item != mBlobMap_.end(); ++item) {
311 CacheHeader* eheader = reinterpret_cast<CacheHeader*>(&buf[offset]);
312 size_t keysize = item->first->dataSize;
313 size_t valuesize = item->second->dataSize;
314 eheader->keySize = keysize;
315 eheader->valueSize = valuesize;
316 if (memcpy_s(eheader->mData, bufsize - offset - headsize, item->first->data, keysize) != 0) {
317 delete[] buf;
318 close(fd);
319 return;
320 }
321
322 if (memcpy_s(eheader->mData + keysize, bufsize - offset - headsize - keysize,
323 item->second->data, valuesize) !=0) {
324 delete[] buf;
325 close(fd);
326 return;
327 }
328 size_t innerSize = headsize + keysize + valuesize;
329 offset += Formatfile(innerSize);
330 }
331 if (memcpy_s(buf, bufsize, CACHE_MAGIC, CACHE_MAGIC_HEAD) != 0) {
332 delete[] buf;
333 close(fd);
334 return;
335 }
336 uint32_t *crc = reinterpret_cast<uint32_t*>(buf + CACHE_MAGIC_HEAD);
337 *crc = CrcGen(buf + CACHE_HEAD, filesize);
338 if (write(fd, buf, bufsize) != -1) {
339 fchmod(fd, S_IRUSR);
340 }
341 delete[] buf;
342 close(fd);
343 }
344
ReadFromDisk()345 void BlobCache::ReadFromDisk()
346 {
347 readStatus_ = true;
348 std::string storefile = cacheDir_ + fileName_;
349 int fd = open(storefile.c_str(), O_RDONLY, 0);
350 if (fd == -1) {
351 close(fd);
352 return;
353 }
354 struct stat bufstat;
355 if (fstat(fd, &bufstat) == -1) {
356 close(fd);
357 return;
358 }
359 if (bufstat.st_size <= 0 || static_cast<size_t>(bufstat.st_size) > maxShaderSize_ + maxShaderSize_) {
360 close(fd);
361 return;
362 }
363 size_t filesize = static_cast<size_t>(bufstat.st_size);
364 uint8_t *buf = reinterpret_cast<uint8_t*>(mmap(nullptr, filesize, PROT_READ, MAP_PRIVATE, fd, 0));
365 if (buf == MAP_FAILED) {
366 close(fd);
367 return;
368 }
369 if (!ValidFile(buf, filesize)) {
370 munmap(buf, filesize);
371 close(fd);
372 return;
373 }
374 size_t headsize = sizeof(CacheHeader);
375 size_t byteoffset = CACHE_HEAD;
376 while (byteoffset < filesize - CACHE_HEAD) {
377 const CacheHeader* eheader = reinterpret_cast<CacheHeader*>(&buf[byteoffset]);
378 size_t keysize = eheader->keySize;
379 size_t valuesize = eheader->valueSize;
380 if (byteoffset + headsize + keysize > filesize) {
381 break;
382 }
383 const uint8_t *key = eheader->mData;
384 if (byteoffset + headsize + keysize + valuesize > filesize) {
385 break;
386 }
387 const uint8_t *value = eheader->mData + keysize;
388 SetBlob(key, static_cast<EGLsizeiANDROID>(keysize), value, static_cast<EGLsizeiANDROID>(valuesize));
389 size_t innerSize = headsize + keysize + valuesize;
390 byteoffset += Formatfile(innerSize);
391 }
392 munmap(buf, filesize);
393 close(fd);
394 }
395
396 //CRC standard function
CrcGen(const uint8_t * buf,size_t len)397 uint32_t BlobCache::CrcGen(const uint8_t *buf, size_t len)
398 {
399 const uint32_t polynoimal = 0xEDB88320;
400 uint32_t crc = 0xFFFFFFFF;
401
402 for (size_t i = 0; i < len ; ++i) {
403 crc ^= (static_cast<uint32_t>(buf[i]) << CRC_NUM);
404 for (size_t j = 0; j < BYTE_SIZE; ++j) {
405 if (crc & 0x80000000) {
406 crc = (crc << 1) ^ polynoimal;
407 } else {
408 crc <<= 1;
409 }
410 }
411 }
412 return crc;
413 }
414
ValidFile(uint8_t * buf,size_t len)415 bool BlobCache::ValidFile(uint8_t *buf, size_t len)
416 {
417 if (memcmp(buf, CACHE_MAGIC, CACHE_MAGIC_HEAD) != 0) {
418 WLOGE("CACHE_MAGIC failed");
419 return false;
420 }
421
422 uint32_t* crcfile = reinterpret_cast<uint32_t*>(buf + CACHE_MAGIC_HEAD);
423 uint32_t crccur = CrcGen(buf + CACHE_HEAD, len - CACHE_HEAD);
424 if (crccur != *crcfile) {
425 WLOGE("crc check failed");
426 return false;
427 }
428
429 return true;
430 }
431
EglSetCacheDir(const std::string dir)432 void EglSetCacheDir(const std::string dir)
433 {
434 BlobCache::Get()->SetCacheDir(dir);
435 }
436 }