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 "shader_cache.h"
17
18 #include <algorithm>
19 #include <array>
20 #include <openssl/sha.h>
21 #include <random>
22 #include <thread>
23 #include "rs_trace.h"
24 #include "utils/log.h"
25
26 namespace OHOS {
27 namespace Rosen {
Instance()28 ShaderCache& ShaderCache::Instance()
29 {
30 static ShaderCache cache_;
31 return cache_;
32 }
33
~ShaderCache()34 ShaderCache::~ShaderCache()
35 {
36 LOGE("ShaderCache: destroying Shadercache");
37 }
38
InitShaderCache(const char * identity,const size_t size,bool isUni)39 void ShaderCache::InitShaderCache(const char* identity, const size_t size, bool isUni)
40 {
41 std::lock_guard<std::mutex> lock(mutex_);
42
43 if (filePath_.length() <= 0) {
44 LOGE("abandon, illegal cacheDir length");
45 return;
46 }
47 cacheData_.reset();
48 size_t totalSize = isUni ? MAX_UNIRENDER_SIZE : MAX_TOTAL_SIZE;
49 cacheData_ = std::make_unique<CacheData>(MAX_KEY_SIZE, MAX_VALUE_SIZE, totalSize, filePath_);
50 cacheData_->ReadFromFile();
51 if (identity == nullptr || size == 0) {
52 LOGE("abandon, illegal cacheDir length");
53 cacheData_->Clear();
54 }
55
56 SHA256_CTX sha256Ctx;
57 SHA256_Init(&sha256Ctx);
58 SHA256_Update(&sha256Ctx, identity, size);
59 idHash_.resize(SHA256_DIGEST_LENGTH);
60 SHA256_Final(idHash_.data(), &sha256Ctx);
61 std::array<uint8_t, SHA256_DIGEST_LENGTH> shaArray;
62 auto key = ID_KEY;
63
64 auto loaded = cacheData_->Get(&key, sizeof(key), shaArray.data(), shaArray.size());
65 if (!(loaded && std::equal(shaArray.begin(), shaArray.end(), idHash_.begin()))) {
66 cacheData_->Clear();
67 LOGW("abandon, bad hash value, cleared for future regeneration");
68 }
69
70 LOGI("shadercache initiation success");
71 initialized_ = true;
72 }
73
SetFilePath(const std::string & filename)74 void ShaderCache::SetFilePath(const std::string& filename)
75 {
76 if (filename.size() == 0) {
77 LOGE("abandon, empty filename");
78 return;
79 }
80 std::lock_guard<std::mutex> lock(mutex_);
81 filePath_ = filename + "/shader_cache";
82 }
83
84 #ifndef USE_ROSEN_DRAWING
load(const SkData & key)85 sk_sp<SkData> ShaderCache::load(const SkData& key)
86 #else
87 std::shared_ptr<Drawing::Data> ShaderCache::Load(const Drawing::Data& key)
88 #endif
89 {
90 #ifndef USE_ROSEN_DRAWING
91 RS_TRACE_NAME("load shader");
92 size_t keySize = key.size();
93 #else
94 RS_TRACE_NAME("Load shader");
95 size_t keySize = key.GetSize();
96 #endif
97 std::lock_guard<std::mutex> lock(mutex_);
98 if (!initialized_) {
99 LOGW("load: failed because ShaderCache is not initialized");
100 return nullptr;
101 }
102
103 void* valueBuffer = malloc(bufferSize_);
104 if (!valueBuffer) {
105 LOGE("load: failed because unable to map memory");
106 return nullptr;
107 }
108 if (!cacheData_) {
109 LOGE("load: cachedata has been destructed");
110 free(valueBuffer);
111 valueBuffer = nullptr;
112 return nullptr;
113 }
114
115 #ifndef USE_ROSEN_DRAWING
116 size_t valueSize = cacheData_->Get(key.data(), keySize, valueBuffer, bufferSize_);
117 #else
118 size_t valueSize = cacheData_->Get(key.GetData(), keySize, valueBuffer, bufferSize_);
119 #endif
120 if (!valueSize) {
121 free(valueBuffer);
122 valueBuffer = nullptr;
123 void* newValueBuffer = realloc(valueBuffer, MAX_VALUE_SIZE);
124 if (!newValueBuffer) {
125 LOGE("load: failed to reallocate maxValueSize");
126 return nullptr;
127 }
128 valueBuffer = newValueBuffer;
129 #ifndef USE_ROSEN_DRAWING
130 valueSize = cacheData_->Get(key.data(), keySize, valueBuffer, bufferSize_);
131 #else
132 valueSize = cacheData_->Get(key.GetData(), keySize, valueBuffer, bufferSize_);
133 #endif
134 }
135
136 if (!valueSize || valueSize > bufferSize_) {
137 LOGE("load: failed to get the cache value with the given key");
138 free(valueBuffer);
139 valueBuffer = nullptr;
140 return nullptr;
141 }
142 #ifndef USE_ROSEN_DRAWING
143 return SkData::MakeFromMalloc(valueBuffer, valueSize);
144 #else
145 auto data = std::make_shared<Drawing::Data>();
146 if (!data->BuildFromMalloc(valueBuffer, valueSize)) {
147 LOGE("load: failed to build drawing data");
148 free(valueBuffer);
149 valueBuffer = nullptr;
150 return nullptr;
151 }
152 return data;
153 #endif
154 }
155
WriteToDisk()156 void ShaderCache::WriteToDisk()
157 {
158 if (!(initialized_ && cacheData_ && savePending_)) {
159 LOGE("abandon: failed to check prerequisites");
160 return;
161 }
162 if (!idHash_.size()) {
163 LOGE("abandon: illegal hash size");
164 return;
165 }
166 auto key = ID_KEY;
167 cacheData_->Rewrite(&key, sizeof(key), idHash_.data(), idHash_.size());
168 cacheData_->WriteToFile();
169 savePending_ = false;
170 }
171
172 #ifndef USE_ROSEN_DRAWING
store(const SkData & key,const SkData & data)173 void ShaderCache::store(const SkData& key, const SkData& data)
174 {
175 RS_TRACE_NAME("store shader");
176 #else
177 void ShaderCache::Store(const Drawing::Data& key, const Drawing::Data& data)
178 {
179 RS_TRACE_NAME("Store shader");
180 #endif
181 std::lock_guard<std::mutex> lock(mutex_);
182
183 if (!initialized_) {
184 LOGW("stored: failed because ShaderCache is not initialized");
185 return;
186 }
187
188 #ifndef USE_ROSEN_DRAWING
189 size_t valueSize = data.size();
190 size_t keySize = key.size();
191 #else
192 size_t valueSize = data.GetSize();
193 size_t keySize = key.GetSize();
194 #endif
195 if (keySize == 0 || valueSize == 0 || valueSize >= MAX_VALUE_SIZE) {
196 LOGE("store: failed because of illegal cache sizes");
197 return;
198 }
199
200 #ifndef USE_ROSEN_DRAWING
201 const void* value = data.data();
202 #else
203 const void* value = data.GetData();
204 #endif
205 cacheDirty_ = true;
206 if (!cacheData_) {
207 LOGE("store: cachedata has been destructed");
208 return;
209 }
210 #ifndef USE_ROSEN_DRAWING
211 cacheData_->Rewrite(key.data(), keySize, value, valueSize);
212 #else
213 cacheData_->Rewrite(key.GetData(), keySize, value, valueSize);
214 #endif
215
216 if (!savePending_ && saveDelaySeconds_ > 0) {
217 savePending_ = true;
218 std::thread deferredSaveThread([this]() {
219 sleep(saveDelaySeconds_);
220 std::lock_guard<std::mutex> lock(mutex_);
221 WriteToDisk();
222 cacheDirty_ = false;
223 });
224 deferredSaveThread.detach();
225 }
226 }
227 } // namespace Rosen
228 } // namespace OHOS