blob: 9ff59efa017cd76310b013753c1f36641527f3e6 [file] [log] [blame]
Stan Ilievd495f432017-10-09 15:49:32 -04001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ShaderCache.h"
Adlai Hollerd2345212020-10-07 14:16:40 -040018#include <GrDirectContext.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050019#include <SkData.h>
rnleece9762b2021-05-21 15:40:53 -070020#include <gui/TraceUtils.h>
Stan Ilievd495f432017-10-09 15:49:32 -040021#include <log/log.h>
Yichi Chen9f959552018-03-29 21:21:54 +080022#include <openssl/sha.h>
John Reck283bb462018-12-13 16:40:14 -080023#include <algorithm>
24#include <array>
25#include <thread>
Stan Ilievd495f432017-10-09 15:49:32 -040026#include "FileBlobCache.h"
Lingfeng Yang3a9f2232018-01-24 10:40:18 -080027#include "Properties.h"
Stan Ilievd495f432017-10-09 15:49:32 -040028
29namespace android {
30namespace uirenderer {
31namespace skiapipeline {
32
33// Cache size limits.
34static const size_t maxKeySize = 1024;
Leon Scroggins III05f5eca2021-06-07 16:09:37 -040035static const size_t maxValueSize = 2 * 1024 * 1024;
Stan Ilievda8a5102019-01-24 14:57:01 -050036static const size_t maxTotalSize = 1024 * 1024;
Stan Ilievd495f432017-10-09 15:49:32 -040037
38ShaderCache::ShaderCache() {
39 // There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
40}
41
42ShaderCache ShaderCache::sCache;
43
44ShaderCache& ShaderCache::get() {
45 return sCache;
46}
47
Yichi Chen9f959552018-03-29 21:21:54 +080048bool ShaderCache::validateCache(const void* identity, ssize_t size) {
John Reck283bb462018-12-13 16:40:14 -080049 if (nullptr == identity && size == 0) return true;
Yichi Chen9f959552018-03-29 21:21:54 +080050
51 if (nullptr == identity || size < 0) {
52 if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
53 ALOGW("ShaderCache::validateCache invalid cache identity");
54 }
55 mBlobCache->clear();
56 return false;
57 }
58
59 SHA256_CTX ctx;
60 SHA256_Init(&ctx);
61
62 SHA256_Update(&ctx, identity, size);
63 mIDHash.resize(SHA256_DIGEST_LENGTH);
64 SHA256_Final(mIDHash.data(), &ctx);
65
66 std::array<uint8_t, SHA256_DIGEST_LENGTH> hash;
67 auto key = sIDKey;
68 auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
69
John Reck283bb462018-12-13 16:40:14 -080070 if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) return true;
Yichi Chen9f959552018-03-29 21:21:54 +080071
72 if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
73 ALOGW("ShaderCache::validateCache cache validation fails");
74 }
75 mBlobCache->clear();
76 return false;
77}
78
79void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) {
Stan Ilievd495f432017-10-09 15:49:32 -040080 ATRACE_NAME("initShaderDiskCache");
81 std::lock_guard<std::mutex> lock(mMutex);
Lingfeng Yang3a9f2232018-01-24 10:40:18 -080082
83 // Emulators can switch between different renders either as part of config
84 // or snapshot migration. Also, program binaries may not work well on some
85 // desktop / laptop GPUs. Thus, disable the shader disk cache for emulator builds.
86 if (!Properties::runningInEmulator && mFilename.length() > 0) {
Stan Ilievd495f432017-10-09 15:49:32 -040087 mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
Yichi Chen9f959552018-03-29 21:21:54 +080088 validateCache(identity, size);
Stan Ilievd495f432017-10-09 15:49:32 -040089 mInitialized = true;
90 }
91}
92
93void ShaderCache::setFilename(const char* filename) {
94 std::lock_guard<std::mutex> lock(mMutex);
95 mFilename = filename;
96}
97
98BlobCache* ShaderCache::getBlobCacheLocked() {
99 LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized");
100 return mBlobCache.get();
101}
102
103sk_sp<SkData> ShaderCache::load(const SkData& key) {
104 ATRACE_NAME("ShaderCache::load");
105 size_t keySize = key.size();
106 std::lock_guard<std::mutex> lock(mMutex);
107 if (!mInitialized) {
Stan Ilievd495f432017-10-09 15:49:32 -0400108 return nullptr;
109 }
110
111 // mObservedBlobValueSize is reasonably big to avoid memory reallocation
112 // Allocate a buffer with malloc. SkData takes ownership of that allocation and will call free.
113 void* valueBuffer = malloc(mObservedBlobValueSize);
114 if (!valueBuffer) {
115 return nullptr;
116 }
117 BlobCache* bc = getBlobCacheLocked();
118 size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
119 int maxTries = 3;
120 while (valueSize > mObservedBlobValueSize && maxTries > 0) {
121 mObservedBlobValueSize = std::min(valueSize, maxValueSize);
John Reck283bb462018-12-13 16:40:14 -0800122 void* newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
Stan Iliev003a9f62018-03-29 13:33:53 -0400123 if (!newValueBuffer) {
124 free(valueBuffer);
Stan Ilievd495f432017-10-09 15:49:32 -0400125 return nullptr;
126 }
Stan Iliev003a9f62018-03-29 13:33:53 -0400127 valueBuffer = newValueBuffer;
Stan Ilievd495f432017-10-09 15:49:32 -0400128 valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
129 maxTries--;
130 }
131 if (!valueSize) {
132 free(valueBuffer);
133 return nullptr;
134 }
135 if (valueSize > mObservedBlobValueSize) {
John Reck283bb462018-12-13 16:40:14 -0800136 ALOGE("ShaderCache::load value size is too big %d", (int)valueSize);
Stan Ilievd495f432017-10-09 15:49:32 -0400137 free(valueBuffer);
138 return nullptr;
139 }
Leon Scroggins III8cedb662022-05-02 10:38:38 -0400140 mNumShadersCachedInRam++;
141 ATRACE_FORMAT("HWUI RAM cache: %d shaders", mNumShadersCachedInRam);
Stan Ilievd495f432017-10-09 15:49:32 -0400142 return SkData::MakeFromMalloc(valueBuffer, valueSize);
143}
144
Leon Scroggins III77644a22022-05-03 15:50:51 -0400145namespace {
146// Helper for BlobCache::set to trace the result.
147void set(BlobCache* cache, const void* key, size_t keySize, const void* value, size_t valueSize) {
148 switch (cache->set(key, keySize, value, valueSize)) {
149 case BlobCache::InsertResult::kInserted:
150 // This is what we expect/hope. It means the cache is large enough.
151 return;
152 case BlobCache::InsertResult::kDidClean: {
153 ATRACE_FORMAT("ShaderCache: evicted an entry to fit {key: %lu value %lu}!", keySize,
154 valueSize);
155 return;
156 }
157 case BlobCache::InsertResult::kNotEnoughSpace: {
158 ATRACE_FORMAT("ShaderCache: could not fit {key: %lu value %lu}!", keySize, valueSize);
159 return;
160 }
161 case BlobCache::InsertResult::kInvalidValueSize:
162 case BlobCache::InsertResult::kInvalidKeySize: {
163 ATRACE_FORMAT("ShaderCache: invalid size {key: %lu value %lu}!", keySize, valueSize);
164 return;
165 }
166 case BlobCache::InsertResult::kKeyTooBig:
167 case BlobCache::InsertResult::kValueTooBig:
168 case BlobCache::InsertResult::kCombinedTooBig: {
169 ATRACE_FORMAT("ShaderCache: entry too big: {key: %lu value %lu}!", keySize, valueSize);
170 return;
171 }
172 }
173}
174} // namespace
175
Yichi Chen9f959552018-03-29 21:21:54 +0800176void ShaderCache::saveToDiskLocked() {
177 ATRACE_NAME("ShaderCache::saveToDiskLocked");
Nolan Scobie193cd962023-02-08 20:03:31 -0500178 if (mInitialized && mBlobCache) {
Yichi Chen9f959552018-03-29 21:21:54 +0800179 if (mIDHash.size()) {
180 auto key = sIDKey;
Leon Scroggins III77644a22022-05-03 15:50:51 -0400181 set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size());
Yichi Chen9f959552018-03-29 21:21:54 +0800182 }
183 mBlobCache->writeToFile();
184 }
Yichi Chen9f959552018-03-29 21:21:54 +0800185}
186
Leon Scroggins III8cedb662022-05-02 10:38:38 -0400187void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
Stan Ilievd495f432017-10-09 15:49:32 -0400188 ATRACE_NAME("ShaderCache::store");
189 std::lock_guard<std::mutex> lock(mMutex);
Leon Scroggins III8cedb662022-05-02 10:38:38 -0400190 mNumShadersCachedInRam++;
191 ATRACE_FORMAT("HWUI RAM cache: %d shaders", mNumShadersCachedInRam);
Stan Ilievd495f432017-10-09 15:49:32 -0400192
193 if (!mInitialized) {
Stan Ilievd495f432017-10-09 15:49:32 -0400194 return;
195 }
196
197 size_t valueSize = data.size();
198 size_t keySize = key.size();
199 if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) {
200 ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize);
201 return;
202 }
203
204 const void* value = data.data();
205
206 BlobCache* bc = getBlobCacheLocked();
Stan Iliev14211aa2019-01-14 12:29:30 -0500207 if (mInStoreVkPipelineInProgress) {
208 if (mOldPipelineCacheSize == -1) {
209 // Record the initial pipeline cache size stored in the file.
210 mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0);
211 }
212 if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) {
213 // There has not been change in pipeline cache size. Stop trying to save.
214 mTryToStorePipelineCache = false;
215 return;
216 }
217 mNewPipelineCacheSize = valueSize;
218 } else {
219 mCacheDirty = true;
220 // If there are new shaders compiled, we probably have new pipeline state too.
221 // Store pipeline cache on the next flush.
222 mNewPipelineCacheSize = -1;
223 mTryToStorePipelineCache = true;
224 }
Leon Scroggins III77644a22022-05-03 15:50:51 -0400225 set(bc, key.data(), keySize, value, valueSize);
Stan Ilievd495f432017-10-09 15:49:32 -0400226
Nolan Scobie193cd962023-02-08 20:03:31 -0500227 if (!mSavePending && mDeferredSaveDelayMs > 0) {
Stan Ilievd495f432017-10-09 15:49:32 -0400228 mSavePending = true;
229 std::thread deferredSaveThread([this]() {
Nolan Scobie193cd962023-02-08 20:03:31 -0500230 usleep(mDeferredSaveDelayMs * 1000); // milliseconds to microseconds
Stan Ilievd495f432017-10-09 15:49:32 -0400231 std::lock_guard<std::mutex> lock(mMutex);
Stan Iliev14211aa2019-01-14 12:29:30 -0500232 // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
233 if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
234 saveToDiskLocked();
235 mOldPipelineCacheSize = mNewPipelineCacheSize;
236 mTryToStorePipelineCache = false;
237 mCacheDirty = false;
238 }
Nolan Scobie193cd962023-02-08 20:03:31 -0500239 mSavePending = false;
Stan Ilievd495f432017-10-09 15:49:32 -0400240 });
241 deferredSaveThread.detach();
242 }
243}
244
Adlai Hollerd2345212020-10-07 14:16:40 -0400245void ShaderCache::onVkFrameFlushed(GrDirectContext* context) {
Stan Iliev14211aa2019-01-14 12:29:30 -0500246 {
247 std::lock_guard<std::mutex> lock(mMutex);
248
249 if (!mInitialized || !mTryToStorePipelineCache) {
250 return;
251 }
252 }
253 mInStoreVkPipelineInProgress = true;
254 context->storeVkPipelineCacheData();
255 mInStoreVkPipelineInProgress = false;
256}
257
Stan Ilievd495f432017-10-09 15:49:32 -0400258} /* namespace skiapipeline */
259} /* namespace uirenderer */
260} /* namespace android */