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