• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #pragma once
18 
19 #include <FileBlobCache.h>
20 #include <GrContextOptions.h>
21 #include <SkRefCnt.h>
22 #include <cutils/compiler.h>
23 #include <ftl/shared_mutex.h>
24 #include <utils/Mutex.h>
25 
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 class GrDirectContext;
31 class SkData;
32 
33 namespace android {
34 
35 class BlobCache;
36 
37 namespace uirenderer {
38 namespace skiapipeline {
39 
40 class ShaderCache : public GrContextOptions::PersistentCache {
41 public:
42     /**
43      * "get" returns a pointer to the singleton ShaderCache object.  This
44      * singleton object will never be destroyed.
45      */
46     static ShaderCache& get();
47 
48     /**
49      * initShaderDiskCache" loads the serialized cache contents from disk,
50      * optionally checks that the on-disk cache matches a provided identity,
51      * and puts the ShaderCache into an initialized state, such that it is
52      * able to insert and retrieve entries from the cache. If identity is
53      * non-null and validation fails, the cache is initialized but contains
54      * no data. If size is less than zero, the cache is initialized but
55      * contains no data.
56      *
57      * This should be called when HWUI pipeline is initialized. When not in
58      * the initialized state the load and store methods will return without
59      * performing any cache operations.
60      */
61     virtual void initShaderDiskCache(const void* identity, ssize_t size);
62 
initShaderDiskCache()63     virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
64 
65     /**
66      * "setFilename" sets the name of the file that should be used to store
67      * cache contents from one program invocation to another. This function does not perform any
68      * disk operation and it should be invoked before "initShaderCache".
69      */
70     virtual void setFilename(const char* filename);
71 
72     /**
73      * "load" attempts to retrieve the value blob associated with a given key
74      * blob from cache.  This will be called by Skia, when it needs to compile a new SKSL shader.
75      */
76     sk_sp<SkData> load(const SkData& key) override;
77 
78     /**
79      * "store" attempts to insert a new key/value blob pair into the cache.
80      * This will be called by Skia after it compiled a new SKSL shader
81      */
82     void store(const SkData& key, const SkData& data, const SkString& description) override;
83 
84     /**
85      * "onVkFrameFlushed" tries to store Vulkan pipeline cache state.
86      * Pipeline cache is saved on disk only if the size of the data has changed or there was
87      * a new shader compiled.
88      */
89     void onVkFrameFlushed(GrDirectContext* context);
90 
91 private:
92     // Creation and (the lack of) destruction is handled internally.
93     ShaderCache();
94 
95     // Copying is disallowed.
96     ShaderCache(const ShaderCache&) = delete;
97     void operator=(const ShaderCache&) = delete;
98 
99     /**
100      * "validateCache" updates the cache to match the given identity.  If the
101      * cache currently has the wrong identity, all entries in the cache are cleared.
102      */
103     bool validateCache(const void* identity, ssize_t size) REQUIRES(mMutex);
104 
105     /**
106      * Helper for BlobCache::set to trace the result and ensure the identity hash
107      * does not get evicted.
108      */
109     void set(const void* key, size_t keySize, const void* value, size_t valueSize) REQUIRES(mMutex);
110 
111     /**
112      * "saveToDiskLocked" attempts to save the current contents of the cache to
113      * disk. If the identity hash exists, we will insert the identity hash into
114      * the cache for next validation.
115      */
116     void saveToDiskLocked() REQUIRES(mMutex);
117 
118     /**
119      * "mInitialized" indicates whether the ShaderCache is in the initialized
120      * state.  It is initialized to false at construction time, and gets set to
121      * true when initialize is called.
122      * When in this state, the cache behaves as normal.  When not,
123      * the load and store methods will return without performing any cache
124      * operations.
125      */
126     bool mInitialized GUARDED_BY(mMutex) = false;
127 
128     /**
129      * "mBlobCache" is the cache in which the key/value blob pairs are stored.
130      * The blob cache contains the Android build number. We treat version mismatches
131      * as an empty cache (logic implemented in BlobCache::unflatten).
132      */
133     std::unique_ptr<FileBlobCache> mBlobCache GUARDED_BY(mMutex);
134 
135     /**
136      * "mFilename" is the name of the file for storing cache contents in between
137      * program invocations.  It is initialized to an empty string at
138      * construction time, and can be set with the setCacheFilename method.  An
139      * empty string indicates that the cache should not be saved to or restored
140      * from disk.
141      */
142     std::string mFilename GUARDED_BY(mMutex);
143 
144     /**
145      * "mIDHash" is the current identity hash for the cache validation. It is
146      * initialized to an empty vector at construction time, and its content is
147      * generated in the call of the validateCache method. An empty vector
148      * indicates that cache validation is not performed, and the hash should
149      * not be stored on disk.
150      */
151     std::vector<uint8_t> mIDHash GUARDED_BY(mMutex);
152 
153     /**
154      * "mSavePending" indicates whether or not a deferred save operation is
155      * pending.  Each time a key/value pair is inserted into the cache via
156      * load, a deferred save is initiated if one is not already pending.
157      * This will wait some amount of time and then trigger a save of the cache
158      * contents to disk, unless mDeferredSaveDelayMs is 0 in which case saving
159      * is disabled.
160      */
161     bool mSavePending GUARDED_BY(mMutex) = false;
162 
163     /**
164      *  "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
165      */
166     size_t mObservedBlobValueSize = 20 * 1024;
167 
168     /**
169      *  The time in milliseconds to wait before saving newly inserted cache entries.
170      *
171      *  WARNING: setting this to 0 will disable writing the cache to disk.
172      */
173     unsigned int mDeferredSaveDelayMs = 4 * 1000;
174 
175     /**
176      * "mMutex" is the shared mutex used to prevent concurrent access to the member
177      * variables. It must be locked whenever the member variables are accessed.
178      */
179     mutable ftl::SharedMutex mMutex;
180 
181     /**
182      *  If set to "true", the next call to onVkFrameFlushed, will invoke
183      * GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
184      */
185     bool mTryToStorePipelineCache GUARDED_BY(mMutex) = true;
186 
187     /**
188      * This flag is used by "ShaderCache::store" to distinguish between shader data and
189      * Vulkan pipeline data.
190      */
191     bool mInStoreVkPipelineInProgress = false;
192 
193     /**
194      *  "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
195      *  to prevent unnecessary disk writes, if the pipeline cache size has not changed.
196      */
197     size_t mNewPipelineCacheSize GUARDED_BY(mMutex) = -1;
198     /**
199      *  "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
200      */
201     size_t mOldPipelineCacheSize GUARDED_BY(mMutex) = -1;
202 
203     /**
204      *  "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
205      */
206     bool mCacheDirty GUARDED_BY(mMutex) = false;
207 
208     /**
209      * "sCache" is the singleton ShaderCache object.
210      */
211     static ShaderCache sCache;
212 
213     /**
214      * "sIDKey" is the cache key of the identity hash
215      */
216     static constexpr uint8_t sIDKey = 0;
217 
218     /**
219      * Most of this class concerns persistent storage for shaders, but it's also
220      * interesting to keep track of how many shaders are stored in RAM. This
221      * class provides a convenient entry point for that.
222      */
223     int mNumShadersCachedInRam GUARDED_BY(mMutex) = 0;
224 
225     friend class ShaderCacheTestUtils;  // used for unit testing
226 };
227 
228 } /* namespace skiapipeline */
229 } /* namespace uirenderer */
230 } /* namespace android */
231