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