1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
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
16 #include "cache_data.h"
17 #include <cerrno>
18 #include <chrono>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <cstring>
22 #include <securec.h>
23 #include "render_context_log.h"
24 #ifdef PRELOAD_SHADER_CACHE
25 #include "shader_cache_utils.h"
26 #endif
27
28 namespace OHOS {
29 const char* RS_CACHE_MAGIC_HEAD = "OHRS";
30 const int RS_CACHE_MAGIC_HEAD_LEN = 4;
31 const int RS_CACHE_HEAD_LEN = 8;
32 const int RS_BYTE_SIZE = 8;
33 const int SHADER_CACHE_CLEAR_LEVEL = 2;
34 const int CHECK_FRAME_FREQUENCY = 10;
35 namespace Rosen {
CacheData(const size_t maxKeySize,const size_t maxValueSize,const size_t maxTotalSize,const std::string & fileName)36 CacheData::CacheData(const size_t maxKeySize, const size_t maxValueSize,
37 const size_t maxTotalSize, const std::string& fileName)
38 : maxKeySize_(maxKeySize),
39 maxValueSize_(maxValueSize),
40 maxTotalSize_(maxTotalSize),
41 cacheDir_(fileName)
42 {
43 softLimit_ = static_cast<size_t>(SHADER_CACHE_SOFT_LIMIT * maxTotalSize);
44 }
45
~CacheData()46 CacheData::~CacheData() {}
47
CrcGen(const uint8_t * buffer,size_t bufferSize)48 uint32_t CacheData::CrcGen(const uint8_t *buffer, size_t bufferSize)
49 {
50 const uint32_t polynoimal = 0xEDB88320;
51 uint32_t crc = 0xFFFFFFFF;
52
53 for (size_t i = 0; i < bufferSize ; ++i) {
54 crc ^= (static_cast<uint32_t>(buffer[i]));
55 for (size_t j = 0; j < RS_BYTE_SIZE; ++j) {
56 if (crc & 0x01) {
57 crc = (crc >> 1) ^ polynoimal;
58 } else {
59 crc >>= 1;
60 }
61 }
62 }
63 return crc ^ 0xFFFFFFFF;
64 }
65
IsValidFile(uint8_t * buffer,size_t bufferSize)66 bool CacheData::IsValidFile(uint8_t *buffer, size_t bufferSize)
67 {
68 if (memcmp(buffer, RS_CACHE_MAGIC_HEAD, RS_CACHE_MAGIC_HEAD_LEN) != 0) {
69 LOGE("abandon, because of mismatched RS_CACHE_MAGIC_HEAD");
70 return false;
71 }
72
73 uint32_t* storedCrc = reinterpret_cast<uint32_t*>(buffer + RS_CACHE_MAGIC_HEAD_LEN);
74 uint32_t computedCrc = CrcGen(buffer + RS_CACHE_HEAD_LEN, bufferSize - RS_CACHE_HEAD_LEN);
75 if (computedCrc != *storedCrc) {
76 LOGE("abandon, because of mismatched crc code");
77 DumpAbnormalCacheToFile(buffer, bufferSize);
78 return false;
79 }
80
81 return true;
82 }
83
DumpAbnormalCacheToFile(uint8_t * buffer,size_t bufferSize)84 void CacheData::DumpAbnormalCacheToFile(uint8_t *buffer, size_t bufferSize)
85 {
86 if (cacheDir_.length() <= 0) {
87 LOGE("dump abnormal cache failed, because of empty filename");
88 return;
89 }
90 char canonicalPath[PATH_MAX] = {0};
91 if (realpath(cacheDir_.c_str(), canonicalPath) == nullptr) {
92 LOGE("dump abnormal cache failed, because of realpath check");
93 return;
94 }
95 std::string abnormalCacheDir = canonicalPath;
96 abnormalCacheDir = abnormalCacheDir + "_abnormal";
97 int fd = open(abnormalCacheDir.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
98 if (fd == ERR_NUMBER) {
99 if (errno == EEXIST) {
100 if (unlink(abnormalCacheDir.c_str()) == ERR_NUMBER) {
101 LOGE("dump abnormal cache failed, because unlinking the existing file fails");
102 return;
103 }
104 fd = open(abnormalCacheDir.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
105 }
106 if (fd == ERR_NUMBER) {
107 LOGE("dump abnormal cache failed, because the file creation fails");
108 return;
109 }
110 }
111
112 std::time_t curTime = time(nullptr);
113 char timestamp[TIME_MAX_LEN] = {0};
114 std::strftime(timestamp, TIME_MAX_LEN, "%Y-%m-%d %H:%M:%S", std::localtime(&curTime));
115 if (write(fd, timestamp, TIME_MAX_LEN) == ERR_NUMBER) {
116 LOGE("dump abnormal cache failed, because fail to write timestamp to disk");
117 close(fd);
118 unlink(abnormalCacheDir.c_str());
119 return;
120 }
121
122 if (write(fd, buffer, bufferSize) == ERR_NUMBER) {
123 LOGE("dump abnormal cache failed, because fail to write data to disk");
124 close(fd);
125 unlink(abnormalCacheDir.c_str());
126 return;
127 }
128 fchmod(fd, S_IRUSR);
129 close(fd);
130 return;
131 }
132
CacheReadFromFile(const std::string filePath)133 void CacheData::CacheReadFromFile(const std::string filePath)
134 {
135 if (filePath.length() <= 0) {
136 LOGD("abandon, because of empty filename.");
137 return;
138 }
139
140 int fd = open(filePath.c_str(), O_RDONLY, 0);
141 if (fd == ERR_NUMBER) {
142 if (errno != ENOENT) {
143 LOGD("abandon, because fail to open file");
144 }
145 return;
146 }
147 struct stat statBuf;
148 if (fstat(fd, &statBuf) == ERR_NUMBER) {
149 LOGD("abandon, because fail to get the file status");
150 close(fd);
151 return;
152 }
153 if (statBuf.st_size < 0) {
154 LOGD("abandon, negative file size");
155 close(fd);
156 return;
157 }
158
159 size_t fileSize = static_cast<size_t>(statBuf.st_size);
160 if (fileSize < RS_CACHE_HEAD_LEN || fileSize > maxTotalSize_ * maxMultipleSize_ + RS_CACHE_HEAD_LEN) {
161 LOGE("abandon, illegal file size");
162 close(fd);
163 return;
164 }
165 uint8_t *buffer = reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
166 if (buffer == MAP_FAILED) {
167 LOGD("abandon, because of mmap failure:");
168 close(fd);
169 return;
170 }
171
172 if (!IsValidFile(buffer, fileSize)) {
173 LOGE("abandon, invalid file");
174 munmap(buffer, fileSize);
175 close(fd);
176 return;
177 }
178
179 uint8_t *shaderBuffer = reinterpret_cast<uint8_t*>(buffer + RS_CACHE_HEAD_LEN);
180 if (DeSerialize(shaderBuffer, fileSize - RS_CACHE_HEAD_LEN) < 0) {
181 LOGE("abandon, because fail to read file contents");
182 }
183 munmap(buffer, fileSize);
184 close(fd);
185 }
186
ReadFromFile()187 void CacheData::ReadFromFile()
188 {
189 #ifdef PRELOAD_SHADER_CACHE
190 // read cache from preload cache dir
191 CacheReadFromFile(ShaderCacheUtils::GetPreloadCacheDir());
192 #endif
193 // read cache from user data dir
194 CacheReadFromFile(cacheDir_);
195 }
196
WriteToFile()197 void CacheData::WriteToFile()
198 {
199 if (cacheDir_.length() <= 0) {
200 LOGD("abandon, because of empty filename.");
201 return;
202 }
203 int fd = open(cacheDir_.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
204 if (fd == ERR_NUMBER) {
205 if (errno == EEXIST) {
206 if (unlink(cacheDir_.c_str()) == ERR_NUMBER) {
207 LOGD("abandon, because unlinking the existing file fails");
208 return;
209 }
210 fd = open(cacheDir_.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
211 }
212 if (fd == ERR_NUMBER) {
213 LOGD("abandon, because the file creation fails");
214 return;
215 }
216 }
217 size_t cacheSize = SerializedSize();
218 if (cacheSize <= 0) {
219 LOGD("abandon, illegal serialized size");
220 close(fd);
221 return;
222 }
223 size_t bufferSize = cacheSize + RS_CACHE_HEAD_LEN;
224 uint8_t *buffer = new uint8_t[bufferSize];
225 if (!buffer) {
226 LOGD("abandon, because fail to allocate buffer for cache content");
227 close(fd);
228 unlink(cacheDir_.c_str());
229 return;
230 }
231 if (Serialize(buffer + RS_CACHE_HEAD_LEN, cacheSize) < 0) {
232 LOGD("abandon, because fail to serialize the CacheData:");
233 delete[] buffer;
234 close(fd);
235 unlink(cacheDir_.c_str());
236 return;
237 }
238
239 // Write the file rs magic head and CRC code
240 if (memcpy_s(buffer, bufferSize, RS_CACHE_MAGIC_HEAD, RS_CACHE_MAGIC_HEAD_LEN) != 0) {
241 delete[] buffer;
242 close(fd);
243 return;
244 }
245 uint32_t *crc = reinterpret_cast<uint32_t*>(buffer + RS_CACHE_MAGIC_HEAD_LEN);
246 *crc = CrcGen(buffer + RS_CACHE_HEAD_LEN, cacheSize);
247
248 if (write(fd, buffer, bufferSize) == ERR_NUMBER) {
249 LOGD("abandon, because fail to write to disk");
250 delete[] buffer;
251 close(fd);
252 unlink(cacheDir_.c_str());
253 return;
254 }
255 delete[] buffer;
256 fchmod(fd, S_IRUSR);
257 close(fd);
258 }
259
Rewrite(const void * key,const size_t keySize,const void * value,const size_t valueSize)260 void CacheData::Rewrite(const void *key, const size_t keySize, const void *value, const size_t valueSize)
261 {
262 if (maxKeySize_ < keySize || maxValueSize_ < valueSize ||
263 maxTotalSize_ < keySize + valueSize || keySize == 0 || valueSize <= 0) {
264 LOGD("abandon, because of illegal content size");
265 return;
266 }
267 std::shared_ptr<DataPointer> fakeDataPointer(std::make_shared<DataPointer>(key, keySize, false));
268 ShaderPointer fakeShaderPointer(fakeDataPointer, nullptr);
269 bool isShaderFound = false;
270 size_t newTotalSize = 0;
271 while (!isShaderFound) {
272 auto index = std::lower_bound(shaderPointers_.begin(), shaderPointers_.end(), fakeShaderPointer);
273 if (index == shaderPointers_.end() || fakeShaderPointer < *index) {
274 std::shared_ptr<DataPointer> keyPointer(std::make_shared<DataPointer>(key, keySize, true));
275 std::shared_ptr<DataPointer> valuePointer(std::make_shared<DataPointer>(value, valueSize, true));
276 newTotalSize = totalSize_ + keySize + valueSize;
277 if (IfSizeValidate(newTotalSize, keySize + valueSize)) {
278 shaderPointers_.insert(index, ShaderPointer(keyPointer, valuePointer));
279 totalSize_ = newTotalSize;
280 break;
281 }
282 if (IfSkipClean(keySize + valueSize)) {
283 break;
284 }
285 if (IfCleanFinished()) {
286 continue;
287 }
288 break;
289 } else {
290 std::shared_ptr<DataPointer> valuePointer(std::make_shared<DataPointer>(value, valueSize, true));
291 std::shared_ptr<DataPointer> oldValuePointer(index->GetValuePointer());
292 newTotalSize = totalSize_ + valueSize - oldValuePointer->GetSize();
293 size_t addedSize = (valueSize > oldValuePointer->GetSize()) ? valueSize - oldValuePointer->GetSize() : 0;
294 if (IfSizeValidate(newTotalSize, addedSize)) {
295 index->SetValue(valuePointer);
296 totalSize_ = newTotalSize;
297 break;
298 }
299 if (IfSkipClean(addedSize)) {
300 break;
301 }
302 if (IfCleanFinished()) {
303 continue;
304 }
305 break;
306 }
307 isShaderFound = true;
308 }
309 cleanThreshold_ = 0;
310 }
311
Get(const void * key,const size_t keySize,void * value,const size_t valueSize)312 std::tuple<CacheData::ErrorCode, size_t> CacheData::Get(const void *key, const size_t keySize,
313 void *value, const size_t valueSize)
314 {
315 if (maxKeySize_ < keySize) {
316 LOGD("abandon, because the key is too large");
317 return {ErrorCode::VALUE_SIZE_OVER_MAX_SIZE, 0};
318 }
319 std::shared_ptr<DataPointer> fakeDataPointer(std::make_shared<DataPointer>(key, keySize, false));
320 ShaderPointer fakeShaderPointer(fakeDataPointer, nullptr);
321 auto index = std::lower_bound(shaderPointers_.begin(), shaderPointers_.end(), fakeShaderPointer);
322 if (index == shaderPointers_.end() || fakeShaderPointer < *index) {
323 LOGD("abandon, because no key is found");
324 return {ErrorCode::KEY_NOT_FOUND, 0};
325 }
326 std::shared_ptr <DataPointer> valuePointer(index->GetValuePointer());
327 size_t valuePointerSize = valuePointer->GetSize();
328 if (valuePointerSize > valueSize) {
329 LOGD("abandon, because of insufficient buffer space");
330 return {ErrorCode::VALUE_SIZE_TOO_SAMLL, valuePointerSize};
331 }
332 if (memcpy_s(value, valueSize, valuePointer->GetData(), valuePointerSize)) {
333 LOGD("abandon, failed to copy content");
334 return {ErrorCode::COPY_FAILED, 0};
335 }
336 return {ErrorCode::NO_ERR, valuePointerSize};
337 }
338
SerializedSize() const339 size_t CacheData::SerializedSize() const
340 {
341 size_t size = Align4(sizeof(Header));
342 for (const ShaderPointer &p: shaderPointers_) {
343 std::shared_ptr <DataPointer> const &keyPointer = p.GetKeyPointer();
344 std::shared_ptr <DataPointer> const &valuePointer = p.GetValuePointer();
345 size += Align4(sizeof(ShaderData) + keyPointer->GetSize() + valuePointer->GetSize());
346 }
347 return size;
348 }
349
Serialize(uint8_t * buffer,const size_t size) const350 int CacheData::Serialize(uint8_t *buffer, const size_t size) const
351 {
352 if (size < sizeof(Header)) {
353 LOGD("abandon because of insufficient buffer space.");
354 return -EINVAL;
355 }
356 Header *header = reinterpret_cast<Header *>(buffer);
357 header->numShaders_ = shaderPointers_.size();
358 size_t byteOffset = Align4(sizeof(Header));
359 size_t headSize = sizeof(ShaderData);
360
361 uint8_t *byteBuffer = reinterpret_cast<uint8_t *>(buffer);
362 for (const ShaderPointer &p: shaderPointers_) {
363 std::shared_ptr<DataPointer> const &keyPointer = p.GetKeyPointer();
364 std::shared_ptr<DataPointer> const &valuePointer = p.GetValuePointer();
365 size_t keySize = keyPointer->GetSize();
366 size_t valueSize = valuePointer->GetSize();
367 size_t pairSize = sizeof(ShaderData) + keySize + valueSize;
368 size_t alignedSize = Align4(pairSize);
369 if (byteOffset + alignedSize > size) {
370 LOGD("abandon because of insufficient buffer space.");
371 return -EINVAL;
372 }
373
374 ShaderData *shaderBuffer = reinterpret_cast<ShaderData *>(&byteBuffer[byteOffset]);
375 shaderBuffer->keySize_ = keySize;
376 shaderBuffer->valueSize_ = valueSize;
377 size_t sizeLeft = size - byteOffset - headSize;
378 if (memcpy_s(shaderBuffer->data_, sizeLeft, keyPointer->GetData(), keySize)) {
379 LOGD("abandon, failed to copy key");
380 return -EINVAL;
381 }
382 if (memcpy_s(shaderBuffer->data_ + keySize, sizeLeft - keySize, valuePointer->GetData(), valueSize)) {
383 LOGD("abandon, failed to copy value");
384 return -EINVAL;
385 }
386 if (alignedSize > pairSize) {
387 auto ret = memset_s(shaderBuffer->data_ + keySize + valueSize, alignedSize - pairSize, 0,
388 alignedSize - pairSize);
389 if (ret != EOK) {
390 LOGD("abandon, failed to memset_s");
391 return -EINVAL;
392 }
393 }
394 byteOffset += alignedSize;
395 }
396 return 0;
397 }
398
DeSerialize(uint8_t const * buffer,const size_t size)399 int CacheData::DeSerialize(uint8_t const *buffer, const size_t size)
400 {
401 shaderPointers_.clear();
402 if (size < sizeof(Header)) {
403 LOGD("abandon, not enough room for cache header");
404 }
405
406 if (buffer == nullptr) {
407 LOGD("abandon, buffer is null");
408 return -EINVAL;
409 }
410 const Header *header = reinterpret_cast<const Header *>(buffer);
411 size_t numShaders = header->numShaders_;
412 size_t byteOffset = Align4(sizeof(Header));
413
414 const uint8_t *byteBuffer = reinterpret_cast<const uint8_t *>(buffer);
415 for (size_t i = 0; i < numShaders; i++) {
416 if (byteOffset + sizeof(ShaderData) > size) {
417 shaderPointers_.clear();
418 LOGD("abandon because of insufficient buffer space");
419 return -EINVAL;
420 }
421 const ShaderData *shaderBuffer = reinterpret_cast<const ShaderData *>(&byteBuffer[byteOffset]);
422 size_t keySize = shaderBuffer->keySize_;
423 size_t valueSize = shaderBuffer->valueSize_;
424 size_t pairSize = sizeof(ShaderData) + keySize + valueSize;
425 size_t alignedSize = Align4(pairSize);
426 if (byteOffset + alignedSize > size) {
427 shaderPointers_.clear();
428 LOGD("abandon, not enough room for cache headers");
429 return -EINVAL;
430 }
431
432 const uint8_t *data = shaderBuffer->data_;
433 Rewrite(data, keySize, data + keySize, valueSize);
434 byteOffset += alignedSize;
435 }
436 return 0;
437 }
438
IfSizeValidate(const size_t newSize,const size_t addedSize) const439 bool CacheData::IfSizeValidate(const size_t newSize, const size_t addedSize) const
440 {
441 // check if size is ok and we don't neet to clean the shaders
442 if (newSize <= maxTotalSize_ || addedSize == 0) {
443 return true;
444 }
445 return false;
446 }
447
IfSkipClean(const size_t addedSize) const448 bool CacheData::IfSkipClean(const size_t addedSize) const
449 {
450 // check if the new shader is still too large after cleaning
451 size_t maxPermittedSize = maxTotalSize_ - maxTotalSize_ / cleanLevel_;
452 if (addedSize > maxPermittedSize) {
453 LOGD("new shader is too large, abandon insert");
454 return true;
455 }
456 return false;
457 }
458
IfCleanFinished()459 bool CacheData::IfCleanFinished()
460 {
461 if (!cleanThreshold_) {
462 RandClean(maxTotalSize_ / cleanLevel_);
463 return true;
464 } else {
465 LOGD("abandon, failed to clean the shaders");
466 return false;
467 }
468 }
469
RandClean(const size_t cleanThreshold)470 void CacheData::RandClean(const size_t cleanThreshold)
471 {
472 if (cleanThreshold == 0) {
473 LOGD("CleanThreshold must be > 0");
474 return;
475 }
476 if (cleanThreshold_ == 0) {
477 if (!CleanInit()) {
478 return;
479 }
480 }
481 cleanThreshold_ = cleanThreshold;
482
483 while (totalSize_ > cleanThreshold_) {
484 if (!StepClean()) {
485 break;
486 }
487 }
488 }
489
CleanInit()490 bool CacheData::CleanInit()
491 {
492 auto now = std::chrono::steady_clock::now().time_since_epoch().count();
493 if (now < 0) {
494 LOGD("abandon, illegal negative now value");
495 return false;
496 }
497 unsigned long currentTime = static_cast<unsigned long>(now);
498 for (int indexRand = 0; indexRand < randLength_; ++indexRand) {
499 cleanInit_[indexRand] = (currentTime >> (indexRand * randShift_)) & 0xFFFF;
500 }
501 return true;
502 }
503
StepClean()504 bool CacheData::StepClean()
505 {
506 long int randIndex = nrand48(cleanInit_);
507 if (randIndex < 0) {
508 LOGD("abandon, illegal negative randIndex value");
509 return false;
510 }
511 size_t sizeRandIndex = static_cast<size_t>(randIndex);
512 size_t currentSize = shaderPointers_.size();
513 if (currentSize == 0) {
514 LOGD("abandon, shader is empty, nothing to clean");
515 return false;
516 }
517 size_t removeIndex = sizeRandIndex % (currentSize);
518 if (!Clean(removeIndex)) {
519 LOGD("abandon, cleaned nothing");
520 return false;
521 }
522 return true;
523 }
524
Clean(const size_t removeIndex)525 size_t CacheData::Clean(const size_t removeIndex)
526 {
527 if (removeIndex >= shaderPointers_.size()) {
528 LOGD("illegal shader index, abandon cleaning");
529 return 0;
530 }
531 const ShaderPointer &shader(shaderPointers_[removeIndex]);
532 size_t reducedSize = shader.GetKeyPointer()->GetSize() + shader.GetValuePointer()->GetSize();
533 totalSize_ -= reducedSize;
534 shaderPointers_.erase(shaderPointers_.begin() + removeIndex);
535 return reducedSize;
536 }
537
CheckShaderCacheOverSoftLimit() const538 bool CacheData::CheckShaderCacheOverSoftLimit() const
539 {
540 return totalSize_ >= softLimit_;
541 }
542
PurgeShaderCacheAfterAnimate(const std::function<bool (void)> & nextFrameHasArrived)543 void CacheData::PurgeShaderCacheAfterAnimate(const std::function<bool(void)>& nextFrameHasArrived)
544 {
545 if (!CleanInit()) {
546 return;
547 }
548 if (!nextFrameHasArrived) {
549 LOGD("nextFrame Func is Empty");
550 return;
551 }
552 const size_t cleanTarget = maxTotalSize_ / SHADER_CACHE_CLEAR_LEVEL;
553 int cleanTimes = 0;
554 while (totalSize_ > cleanTarget && (cleanTimes % CHECK_FRAME_FREQUENCY != 0 || !nextFrameHasArrived())) {
555 if (!StepClean()) {
556 break;
557 }
558 cleanTimes++;
559 }
560 }
561
DataPointer(const void * data,size_t size,bool ifOccupy)562 CacheData::DataPointer::DataPointer(const void *data, size_t size, bool ifOccupy)
563 : pointer_(nullptr),
564 size_(size),
565 toFree_(ifOccupy)
566 {
567 if (ifOccupy) {
568 pointer_ = malloc(size);
569 } else {
570 pointer_ = data;
571 }
572
573 if (data != nullptr && ifOccupy) {
574 if (memcpy_s(const_cast<void *>(pointer_), size, data, size)) {
575 LOGD("abandon: failed to copy data");
576 return;
577 }
578 }
579 }
580
~DataPointer()581 CacheData::DataPointer::~DataPointer()
582 {
583 if (toFree_) {
584 free(const_cast<void *>(pointer_));
585 pointer_ = nullptr;
586 }
587 }
588
ShaderPointer()589 CacheData::ShaderPointer::ShaderPointer() {}
590
ShaderPointer(const std::shared_ptr<DataPointer> & key,const std::shared_ptr<DataPointer> & value)591 CacheData::ShaderPointer::ShaderPointer(const std::shared_ptr <DataPointer> &key,
592 const std::shared_ptr <DataPointer> &value)
593 : keyPointer_(key),
594 valuePointer_(value) {}
595
ShaderPointer(const ShaderPointer & sp)596 CacheData::ShaderPointer::ShaderPointer(const ShaderPointer &sp)
597 : keyPointer_(sp.keyPointer_),
598 valuePointer_(sp.valuePointer_) {}
599 } // namespace Rosen
600 } // namespace OHOS