Add ability to write out VkPipelineCache to gpu PersistentCache.

Bug: skia:
Change-Id: Id13d680401f69a074ae0c85f9ceaf3308fccb129
Reviewed-on: https://skia-review.googlesource.com/c/181403
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 060bc87..5ca2330 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -36,22 +36,55 @@
     delete fPipelineStateCache;
 }
 
-void GrVkResourceProvider::init() {
-    VkPipelineCacheCreateInfo createInfo;
-    memset(&createInfo, 0, sizeof(VkPipelineCacheCreateInfo));
-    createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
-    createInfo.pNext = nullptr;
-    createInfo.flags = 0;
-    createInfo.initialDataSize = 0;
-    createInfo.pInitialData = nullptr;
-    VkResult result = GR_VK_CALL(fGpu->vkInterface(),
-                                 CreatePipelineCache(fGpu->device(), &createInfo, nullptr,
-                                                     &fPipelineCache));
-    SkASSERT(VK_SUCCESS == result);
-    if (VK_SUCCESS != result) {
-        fPipelineCache = VK_NULL_HANDLE;
-    }
+VkPipelineCache GrVkResourceProvider::pipelineCache() {
+    if (fPipelineCache == VK_NULL_HANDLE) {
+        VkPipelineCacheCreateInfo createInfo;
+        memset(&createInfo, 0, sizeof(VkPipelineCacheCreateInfo));
+        createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+        createInfo.pNext = nullptr;
+        createInfo.flags = 0;
 
+        auto persistentCache = fGpu->getContext()->contextPriv().getPersistentCache();
+        sk_sp<SkData> cached;
+        if (persistentCache) {
+            uint32_t key = GrVkGpu::kPipelineCache_PersistentCacheKeyType;
+            sk_sp<SkData> keyData = SkData::MakeWithoutCopy(&key, sizeof(uint32_t));
+            cached = persistentCache->load(*keyData);
+        }
+        bool usedCached = false;
+        if (cached) {
+            uint32_t* cacheHeader = (uint32_t*)cached->data();
+            if (cacheHeader[1] == VK_PIPELINE_CACHE_HEADER_VERSION_ONE) {
+                // For version one of the header, the total header size is 16 bytes plus
+                // VK_UUID_SIZE bytes. See Section 9.6 (Pipeline Cache) in the vulkan spec to see
+                // the breakdown of these bytes.
+                SkASSERT(cacheHeader[0] == 16 + VK_UUID_SIZE);
+                const VkPhysicalDeviceProperties& devProps = fGpu->physicalDeviceProperties();
+                const uint8_t* supportedPipelineCacheUUID = devProps.pipelineCacheUUID;
+                if (cacheHeader[2] == devProps.vendorID && cacheHeader[3] == devProps.deviceID &&
+                    !memcmp(&cacheHeader[4], supportedPipelineCacheUUID, VK_UUID_SIZE)) {
+                    createInfo.initialDataSize = cached->size();
+                    createInfo.pInitialData = cached->data();
+                    usedCached = true;
+                }
+            }
+        }
+        if (!usedCached) {
+            createInfo.initialDataSize = 0;
+            createInfo.pInitialData = nullptr;
+        }
+        VkResult result = GR_VK_CALL(fGpu->vkInterface(),
+                                     CreatePipelineCache(fGpu->device(), &createInfo, nullptr,
+                                                         &fPipelineCache));
+        SkASSERT(VK_SUCCESS == result);
+        if (VK_SUCCESS != result) {
+            fPipelineCache = VK_NULL_HANDLE;
+        }
+    }
+    return fPipelineCache;
+}
+
+void GrVkResourceProvider::init() {
     // Init uniform descriptor objects
     GrVkDescriptorSetManager* dsm = GrVkDescriptorSetManager::CreateUniformManager(fGpu);
     fDescriptorSetManagers.emplace_back(dsm);
@@ -69,7 +102,7 @@
                                                    VkPipelineLayout layout) {
     return GrVkPipeline::Create(fGpu, primProc, pipeline, stencil, shaderStageInfo,
                                 shaderStageCount, primitiveType, compatibleRenderPass, layout,
-                                fPipelineCache);
+                                this->pipelineCache());
 }
 
 GrVkCopyPipeline* GrVkResourceProvider::findOrCreateCopyPipeline(
@@ -88,7 +121,7 @@
                                             pipelineLayout,
                                             dst->numColorSamples(),
                                             *dst->simpleRenderPass(),
-                                            fPipelineCache);
+                                            this->pipelineCache());
         if (!pipeline) {
             return nullptr;
         }
@@ -491,6 +524,28 @@
     fAvailableCommandPools.push_back(pool);
 }
 
+void GrVkResourceProvider::storePipelineCacheData() {
+    size_t dataSize = 0;
+    VkResult result = GR_VK_CALL(fGpu->vkInterface(), GetPipelineCacheData(fGpu->device(),
+                                                                           this->pipelineCache(),
+                                                                           &dataSize, nullptr));
+    SkASSERT(result == VK_SUCCESS);
+
+    std::unique_ptr<uint8_t[]> data(new uint8_t[dataSize]);
+
+    result = GR_VK_CALL(fGpu->vkInterface(), GetPipelineCacheData(fGpu->device(),
+                                                                  this->pipelineCache(),
+                                                                  &dataSize,
+                                                                  (void*)data.get()));
+    SkASSERT(result == VK_SUCCESS);
+
+    uint32_t key = GrVkGpu::kPipelineCache_PersistentCacheKeyType;
+    sk_sp<SkData> keyData = SkData::MakeWithoutCopy(&key, sizeof(uint32_t));
+
+    fGpu->getContext()->contextPriv().getPersistentCache()->store(
+            *keyData, *SkData::MakeWithoutCopy(data.get(), dataSize));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 GrVkResourceProvider::CompatibleRenderPassSet::CompatibleRenderPassSet(