blob: a55de95035a707323a96ab545fe2f6573c42aba4 [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");
178 if (mInitialized && mBlobCache && mSavePending) {
179 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 }
185 mSavePending = false;
186}
187
Leon Scroggins III8cedb662022-05-02 10:38:38 -0400188void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
Stan Ilievd495f432017-10-09 15:49:32 -0400189 ATRACE_NAME("ShaderCache::store");
190 std::lock_guard<std::mutex> lock(mMutex);
Leon Scroggins III8cedb662022-05-02 10:38:38 -0400191 mNumShadersCachedInRam++;
192 ATRACE_FORMAT("HWUI RAM cache: %d shaders", mNumShadersCachedInRam);
Stan Ilievd495f432017-10-09 15:49:32 -0400193
194 if (!mInitialized) {
Stan Ilievd495f432017-10-09 15:49:32 -0400195 return;
196 }
197
198 size_t valueSize = data.size();
199 size_t keySize = key.size();
200 if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) {
201 ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize);
202 return;
203 }
204
205 const void* value = data.data();
206
207 BlobCache* bc = getBlobCacheLocked();
Stan Iliev14211aa2019-01-14 12:29:30 -0500208 if (mInStoreVkPipelineInProgress) {
209 if (mOldPipelineCacheSize == -1) {
210 // Record the initial pipeline cache size stored in the file.
211 mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0);
212 }
213 if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) {
214 // There has not been change in pipeline cache size. Stop trying to save.
215 mTryToStorePipelineCache = false;
216 return;
217 }
218 mNewPipelineCacheSize = valueSize;
219 } else {
220 mCacheDirty = true;
221 // If there are new shaders compiled, we probably have new pipeline state too.
222 // Store pipeline cache on the next flush.
223 mNewPipelineCacheSize = -1;
224 mTryToStorePipelineCache = true;
225 }
Leon Scroggins III77644a22022-05-03 15:50:51 -0400226 set(bc, key.data(), keySize, value, valueSize);
Stan Ilievd495f432017-10-09 15:49:32 -0400227
228 if (!mSavePending && mDeferredSaveDelay > 0) {
229 mSavePending = true;
230 std::thread deferredSaveThread([this]() {
231 sleep(mDeferredSaveDelay);
232 std::lock_guard<std::mutex> lock(mMutex);
Stan Iliev14211aa2019-01-14 12:29:30 -0500233 // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
234 if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
235 saveToDiskLocked();
236 mOldPipelineCacheSize = mNewPipelineCacheSize;
237 mTryToStorePipelineCache = false;
238 mCacheDirty = false;
239 }
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 */