Home | History | Annotate | Download | only in skia
      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 
     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