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