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/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 18835ba..46c1675 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -1517,6 +1517,9 @@
             context->contextPriv().getGpu()->deleteTestingOnlyBackendRenderTarget(backendRT);
         }
     }
+    if (grOptions.fPersistentCache) {
+        context->storeVkPipelineCacheData();
+    }
     return "";
 }
 
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 0be9854..5c3781f 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -283,6 +283,8 @@
 
     bool supportsDistanceFieldText() const;
 
+    void storeVkPipelineCacheData();
+
 protected:
     GrContext(GrBackendApi, int32_t id = SK_InvalidGenID);
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 14a65fa..88d1ec2 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -397,6 +397,16 @@
     fContext->fDrawingManager->flush(proxy);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+void GrContext::storeVkPipelineCacheData() {
+    if (fGpu) {
+        fGpu->storeVkPipelineCacheData();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 // TODO: This will be removed when GrSurfaceContexts are aware of their color types.
 // (skbug.com/6718)
 static bool valid_premul_config(GrPixelConfig config) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 850934e..c99733d 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -429,6 +429,8 @@
         return 0;
     }
 
+    virtual void storeVkPipelineCacheData() {}
+
 protected:
     // Handles cases where a surface will be updated without a call to flushRenderTarget.
     void didWriteToSurface(GrSurface* surface, GrSurfaceOrigin origin, const SkIRect* bounds,
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 59764ad..1bb1062 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -170,10 +170,11 @@
     VK_CALL(GetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &fPhysDevProps));
     VK_CALL(GetPhysicalDeviceMemoryProperties(backendContext.fPhysicalDevice, &fPhysDevMemProps));
 
+    fResourceProvider.init();
+
     fCmdPool = fResourceProvider.findOrCreateCommandPool();
     fCurrentCmdBuffer = fCmdPool->getPrimaryCommandBuffer();
     SkASSERT(fCurrentCmdBuffer);
-    fResourceProvider.init();
     fCurrentCmdBuffer->begin(this);
 }
 
@@ -2203,3 +2204,9 @@
     return sampler->uniqueID();
 }
 
+void GrVkGpu::storeVkPipelineCacheData() {
+    if (this->getContext()->contextPriv().getPersistentCache()) {
+        this->resourceProvider().storePipelineCacheData();
+    }
+}
+
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 22cf45e..acd814c 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -57,10 +57,10 @@
     VkQueue  queue() const { return fQueue; }
     uint32_t  queueIndex() const { return fQueueIndex; }
     GrVkCommandPool* cmdPool() const { return fCmdPool; }
-    VkPhysicalDeviceProperties physicalDeviceProperties() const {
+    const VkPhysicalDeviceProperties& physicalDeviceProperties() const {
         return fPhysDevProps;
     }
-    VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties() const {
+    const VkPhysicalDeviceMemoryProperties& physicalDeviceMemoryProperties() const {
         return fPhysDevMemProps;
     }
 
@@ -166,6 +166,13 @@
     uint32_t getExtraSamplerKeyForProgram(const GrSamplerState&,
                                           const GrBackendFormat& format) override;
 
+    enum PersistentCacheKeyType : uint32_t {
+        kShader_PersistentCacheKeyType = 0,
+        kPipelineCache_PersistentCacheKeyType = 1,
+    };
+
+    void storeVkPipelineCacheData() override;
+
 private:
     GrVkGpu(GrContext*, const GrContextOptions&, const GrVkBackendContext&,
             sk_sp<const GrVkInterface>);
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index fab4559..6219277 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -183,8 +183,10 @@
                                                    const SkSL::Program::Inputs& fragInputs,
                                                    const SkSL::String& geom,
                                                    const SkSL::Program::Inputs& geomInputs) {
+    Desc* desc = static_cast<Desc*>(this->desc());
+
     // see loadShadersFromCache for the layout of cache entries
-    sk_sp<SkData> key = SkData::MakeWithoutCopy(desc()->asKey(), desc()->keyLength());
+    sk_sp<SkData> key = SkData::MakeWithoutCopy(desc->asKey(), desc->shaderKeyLength());
     size_t dataLength = (sizeof(shader_size) + sizeof(SkSL::Program::Inputs)) * 3 + vert.length() +
                         frag.length() + geom.length();
     std::unique_ptr<uint8_t[]> data(new uint8_t[dataLength]);
@@ -276,7 +278,7 @@
     sk_sp<SkData> cached;
     auto persistentCache = fGpu->getContext()->contextPriv().getPersistentCache();
     if (persistentCache) {
-        sk_sp<SkData> key = SkData::MakeWithoutCopy(desc->asKey(), desc->keyLength());
+        sk_sp<SkData> key = SkData::MakeWithoutCopy(desc->asKey(), desc->shaderKeyLength());
         cached = persistentCache->load(*key);
     }
     int numShaderStages;
@@ -395,6 +397,12 @@
     }
 
     GrProcessorKeyBuilder b(&desc->key());
+
+    b.add32(GrVkGpu::kShader_PersistentCacheKeyType);
+    int keyLength = desc->key().count();
+    SkASSERT(0 == (keyLength % 4));
+    desc->fShaderKeyLength = SkToU32(keyLength);
+
     GrVkRenderTarget* vkRT = (GrVkRenderTarget*)pipeline.renderTarget();
     vkRT->simpleRenderPass()->genKey(&b);
 
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.h b/src/gpu/vk/GrVkPipelineStateBuilder.h
index fc322df..7351789 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.h
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.h
@@ -44,7 +44,11 @@
                           GrPrimitiveType primitiveType,
                           GrVkGpu* gpu);
 
+        size_t shaderKeyLength() const { return fShaderKeyLength; }
+
     private:
+        size_t fShaderKeyLength;
+
         typedef GrProgramDesc INHERITED;
     };
 
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(
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 2eac2b2..97f11b1 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -156,6 +156,8 @@
     // can be reused by the next uniform buffer resource request.
     void recycleStandardUniformBufferResource(const GrVkResource*);
 
+    void storePipelineCacheData();
+
     // Destroy any cached resources. To be called before destroying the VkDevice.
     // The assumption is that all queues are idle and all command buffers are finished.
     // For resource tracing to work properly, this should be called after unrefing all other
@@ -245,6 +247,8 @@
         int                           fLastReturnedIndex;
     };
 
+    VkPipelineCache pipelineCache();
+
     GrVkGpu* fGpu;
 
     // Central cache for creating pipelines