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     ANDROID_API 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) 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(GrContext* 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.
157      */
158     bool mSavePending = false;
159 
160     /**
161      *  "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
162      */
163     size_t mObservedBlobValueSize = 20 * 1024;
164 
165     /**
166      *  The time in seconds to wait before saving newly inserted cache entries.
167      */
168     unsigned int mDeferredSaveDelay = 4;
169 
170     /**
171      * "mMutex" is the mutex used to prevent concurrent access to the member
172      * variables. It must be locked whenever the member variables are accessed.
173      */
174     mutable std::mutex mMutex;
175 
176     /**
177      *  If set to "true", the next call to onVkFrameFlushed, will invoke
178      * GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
179      */
180     bool mTryToStorePipelineCache = true;
181 
182     /**
183      * This flag is used by "ShaderCache::store" to distinguish between shader data and
184      * Vulkan pipeline data.
185      */
186     bool mInStoreVkPipelineInProgress = false;
187 
188     /**
189      *  "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
190      *  to prevent unnecessary disk writes, if the pipeline cache size has not changed.
191      */
192     size_t mNewPipelineCacheSize = -1;
193     /**
194      *  "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
195      */
196     size_t mOldPipelineCacheSize = -1;
197 
198     /**
199      *  "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
200      */
201     bool mCacheDirty = false;
202 
203     /**
204      * "sCache" is the singleton ShaderCache object.
205      */
206     static ShaderCache sCache;
207 
208     /**
209      * "sIDKey" is the cache key of the identity hash
210      */
211     static constexpr uint8_t sIDKey = 0;
212 
213     friend class ShaderCacheTestUtils;  // used for unit testing
214 };
215 
216 } /* namespace skiapipeline */
217 } /* namespace uirenderer */
218 } /* namespace android */
219