Perform Vulkan resets in a background thread

Bug: skia:
Change-Id: I90783dc9ac2fcae560cd54e6c8c6df11d7195ac0
Reviewed-on: https://skia-review.googlesource.com/c/168488
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 915e06f..81ac5a2 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -584,6 +584,8 @@
   "$_src/gpu/vk/GrVkCaps.h",
   "$_src/gpu/vk/GrVkCommandBuffer.cpp",
   "$_src/gpu/vk/GrVkCommandBuffer.h",
+  "$_src/gpu/vk/GrVkCommandPool.cpp",
+  "$_src/gpu/vk/GrVkCommandPool.h",
   "$_src/gpu/vk/GrVkCopyManager.cpp",
   "$_src/gpu/vk/GrVkCopyManager.h",
   "$_src/gpu/vk/GrVkCopyPipeline.cpp",
diff --git a/src/gpu/vk/GrVkBuffer.cpp b/src/gpu/vk/GrVkBuffer.cpp
index b3c1d82..f8c120d 100644
--- a/src/gpu/vk/GrVkBuffer.cpp
+++ b/src/gpu/vk/GrVkBuffer.cpp
@@ -102,7 +102,7 @@
     gpu->addBufferMemoryBarrier(srcStageMask, dstStageMask, byRegion, &bufferMemoryBarrier);
 }
 
-void GrVkBuffer::Resource::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkBuffer::Resource::freeGPUData(GrVkGpu* gpu) const {
     SkASSERT(fBuffer);
     SkASSERT(fAlloc.fMemory);
     VK_CALL(gpu, DestroyBuffer(gpu->device(), fBuffer, nullptr));
diff --git a/src/gpu/vk/GrVkBuffer.h b/src/gpu/vk/GrVkBuffer.h
index 987c50b..f534734 100644
--- a/src/gpu/vk/GrVkBuffer.h
+++ b/src/gpu/vk/GrVkBuffer.h
@@ -71,7 +71,7 @@
         Type               fType;
 
     private:
-        void freeGPUData(const GrVkGpu* gpu) const override;
+        void freeGPUData(GrVkGpu* gpu) const override;
 
         void onRecycle(GrVkGpu* gpu) const override { this->unref(gpu); }
 
diff --git a/src/gpu/vk/GrVkBufferView.cpp b/src/gpu/vk/GrVkBufferView.cpp
index b7e35c7..860cd68 100644
--- a/src/gpu/vk/GrVkBufferView.cpp
+++ b/src/gpu/vk/GrVkBufferView.cpp
@@ -33,6 +33,6 @@
     return new GrVkBufferView(bufferView);
 }
 
-void GrVkBufferView::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkBufferView::freeGPUData(GrVkGpu* gpu) const {
     GR_VK_CALL(gpu->vkInterface(), DestroyBufferView(gpu->device(), fBufferView, nullptr));
 }
diff --git a/src/gpu/vk/GrVkBufferView.h b/src/gpu/vk/GrVkBufferView.h
index 24f128f..439a93d 100644
--- a/src/gpu/vk/GrVkBufferView.h
+++ b/src/gpu/vk/GrVkBufferView.h
@@ -29,7 +29,7 @@
 private:
     GrVkBufferView(VkBufferView bufferView) : INHERITED(), fBufferView(bufferView) {}
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
 
     VkBufferView  fBufferView;
 
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 7b4a92d..6773269 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkCommandBuffer.h"
 
+#include "GrVkCommandPool.h"
 #include "GrVkGpu.h"
 #include "GrVkFramebuffer.h"
 #include "GrVkImage.h"
@@ -40,7 +41,7 @@
     }
 }
 
-void GrVkCommandBuffer::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkCommandBuffer::freeGPUData(GrVkGpu* gpu) const {
     SkASSERT(!fIsActive);
     for (int i = 0; i < fTrackedResources.count(); ++i) {
         fTrackedResources[i]->unref(gpu);
@@ -54,13 +55,14 @@
         fTrackedRecordingResources[i]->unref(gpu);
     }
 
-    GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), gpu->cmdPool(),
-                                                      1, &fCmdBuffer));
+    GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), fCmdPool->vkCommandPool(), 1,
+                                                      &fCmdBuffer));
 
     this->onFreeGPUData(gpu);
 }
 
 void GrVkCommandBuffer::abandonGPUData() const {
+    SkDEBUGCODE(fResourcesReleased = true;)
     for (int i = 0; i < fTrackedResources.count(); ++i) {
         fTrackedResources[i]->unrefAndAbandon();
     }
@@ -73,9 +75,12 @@
     for (int i = 0; i < fTrackedRecordingResources.count(); ++i) {
         fTrackedRecordingResources[i]->unrefAndAbandon();
     }
+
+    this->onAbandonGPUData();
 }
 
-void GrVkCommandBuffer::reset(GrVkGpu* gpu) {
+void GrVkCommandBuffer::releaseResources(GrVkGpu* gpu) {
+    SkDEBUGCODE(fResourcesReleased = true;)
     SkASSERT(!fIsActive);
     for (int i = 0; i < fTrackedResources.count(); ++i) {
         fTrackedResources[i]->unref(gpu);
@@ -102,14 +107,9 @@
         fTrackedRecordingResources.rewind();
     }
 
-
     this->invalidateState();
 
-    // we will retain resources for later use
-    VkCommandBufferResetFlags flags = 0;
-    GR_VK_CALL(gpu->vkInterface(), ResetCommandBuffer(fCmdBuffer, flags));
-
-    this->onReset(gpu);
+    this->onReleaseResources(gpu);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -358,11 +358,11 @@
 }
 
 GrVkPrimaryCommandBuffer* GrVkPrimaryCommandBuffer::Create(const GrVkGpu* gpu,
-                                                           VkCommandPool cmdPool) {
+                                                           GrVkCommandPool* cmdPool) {
     const VkCommandBufferAllocateInfo cmdInfo = {
         VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
         nullptr,                                          // pNext
-        cmdPool,                                          // commandPool
+        cmdPool->vkCommandPool(),                         // commandPool
         VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
         1                                                 // bufferCount
     };
@@ -374,7 +374,7 @@
     if (err) {
         return nullptr;
     }
-    return new GrVkPrimaryCommandBuffer(cmdBuffer);
+    return new GrVkPrimaryCommandBuffer(cmdBuffer, cmdPool);
 }
 
 void GrVkPrimaryCommandBuffer::begin(const GrVkGpu* gpu) {
@@ -391,7 +391,7 @@
     fIsActive = true;
 }
 
-void GrVkPrimaryCommandBuffer::end(const GrVkGpu* gpu) {
+void GrVkPrimaryCommandBuffer::end(GrVkGpu* gpu) {
     SkASSERT(fIsActive);
     SkASSERT(!fActiveRenderPass);
     GR_VK_CALL_ERRCHECK(gpu->vkInterface(), EndCommandBuffer(fCmdBuffer));
@@ -445,6 +445,10 @@
 
 void GrVkPrimaryCommandBuffer::executeCommands(const GrVkGpu* gpu,
                                                GrVkSecondaryCommandBuffer* buffer) {
+    // The Vulkan spec allows secondary command buffers to be executed on a primary command buffer
+    // if the command pools both were created from were created with the same queue family. However,
+    // we currently always create them from the same pool.
+    SkASSERT(buffer->commandPool() == fCmdPool);
     SkASSERT(fIsActive);
     SkASSERT(!buffer->fIsActive);
     SkASSERT(fActiveRenderPass);
@@ -565,6 +569,7 @@
 }
 
 bool GrVkPrimaryCommandBuffer::finished(const GrVkGpu* gpu) const {
+    SkASSERT(!fIsActive);
     if (VK_NULL_HANDLE == fSubmitFence) {
         return true;
     }
@@ -586,9 +591,16 @@
     return false;
 }
 
-void GrVkPrimaryCommandBuffer::onReset(GrVkGpu* gpu) {
+void GrVkPrimaryCommandBuffer::onReleaseResources(GrVkGpu* gpu) {
     for (int i = 0; i < fSecondaryCommandBuffers.count(); ++i) {
-        gpu->resourceProvider().recycleSecondaryCommandBuffer(fSecondaryCommandBuffers[i]);
+        fSecondaryCommandBuffers[i]->releaseResources(gpu);
+    }
+}
+
+void GrVkPrimaryCommandBuffer::recycleSecondaryCommandBuffers() {
+    for (int i = 0; i < fSecondaryCommandBuffers.count(); ++i) {
+        SkASSERT(fSecondaryCommandBuffers[i]->commandPool() == fCmdPool);
+        fCmdPool->recycleSecondaryCommandBuffer(fSecondaryCommandBuffers[i]);
     }
     fSecondaryCommandBuffers.reset();
 }
@@ -790,12 +802,22 @@
                                                    regions));
 }
 
-void GrVkPrimaryCommandBuffer::onFreeGPUData(const GrVkGpu* gpu) const {
+void GrVkPrimaryCommandBuffer::onFreeGPUData(GrVkGpu* gpu) const {
     SkASSERT(!fActiveRenderPass);
     // Destroy the fence, if any
     if (VK_NULL_HANDLE != fSubmitFence) {
         GR_VK_CALL(gpu->vkInterface(), DestroyFence(gpu->device(), fSubmitFence, nullptr));
     }
+    for (GrVkSecondaryCommandBuffer* buffer : fSecondaryCommandBuffers) {
+        buffer->unref(gpu);
+    }
+}
+
+void GrVkPrimaryCommandBuffer::onAbandonGPUData() const {
+    SkASSERT(!fActiveRenderPass);
+    for (GrVkSecondaryCommandBuffer* buffer : fSecondaryCommandBuffers) {
+        buffer->unrefAndAbandon();
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -803,11 +825,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(const GrVkGpu* gpu,
-                                                               VkCommandPool cmdPool) {
+                                                               GrVkCommandPool* cmdPool) {
     const VkCommandBufferAllocateInfo cmdInfo = {
         VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
         nullptr,                                          // pNext
-        cmdPool,                                          // commandPool
+        cmdPool->vkCommandPool(),                         // commandPool
         VK_COMMAND_BUFFER_LEVEL_SECONDARY,                // level
         1                                                 // bufferCount
     };
@@ -819,7 +841,7 @@
     if (err) {
         return nullptr;
     }
-    return new GrVkSecondaryCommandBuffer(cmdBuffer);
+    return new GrVkSecondaryCommandBuffer(cmdBuffer, cmdPool);
 }
 
 
@@ -853,10 +875,9 @@
     fIsActive = true;
 }
 
-void GrVkSecondaryCommandBuffer::end(const GrVkGpu* gpu) {
+void GrVkSecondaryCommandBuffer::end(GrVkGpu* gpu) {
     SkASSERT(fIsActive);
     GR_VK_CALL_ERRCHECK(gpu->vkInterface(), EndCommandBuffer(fCmdBuffer));
     this->invalidateState();
     fIsActive = false;
 }
-
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index 6f5da0e..039f4d0 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -71,6 +71,8 @@
                             uint32_t dynamicOffsetCount,
                             const uint32_t* dynamicOffsets);
 
+    GrVkCommandPool* commandPool() { return fCmdPool; }
+
     void setViewport(const GrVkGpu* gpu,
                      uint32_t firstViewport,
                      uint32_t viewportCount,
@@ -124,13 +126,15 @@
         fTrackedRecordingResources.append(1, &resource);
     }
 
-    void reset(GrVkGpu* gpu);
+    void releaseResources(GrVkGpu* gpu);
 
 protected:
-        GrVkCommandBuffer(VkCommandBuffer cmdBuffer, const GrVkRenderPass* rp = VK_NULL_HANDLE)
+        GrVkCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool,
+                          const GrVkRenderPass* rp = VK_NULL_HANDLE)
             : fIsActive(false)
             , fActiveRenderPass(rp)
             , fCmdBuffer(cmdBuffer)
+            , fCmdPool(cmdPool)
             , fNumResets(0) {
             fTrackedResources.setReserve(kInitialTrackedResourcesCount);
             fTrackedRecycledResources.setReserve(kInitialTrackedResourcesCount);
@@ -153,14 +157,19 @@
 
         VkCommandBuffer           fCmdBuffer;
 
+        // Raw pointer, not refcounted. The command pool controls the command buffer's lifespan, so
+        // it's guaranteed to outlive us.
+        GrVkCommandPool*          fCmdPool;
+
 private:
     static const int kInitialTrackedResourcesCount = 32;
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
-    virtual void onFreeGPUData(const GrVkGpu* gpu) const = 0;
-    void abandonGPUData() const override;
+    void freeGPUData(GrVkGpu* gpu) const final override;
+    virtual void onFreeGPUData(GrVkGpu* gpu) const = 0;
+    void abandonGPUData() const final override;
+    virtual void onAbandonGPUData() const = 0;
 
-    virtual void onReset(GrVkGpu* gpu) {}
+    virtual void onReleaseResources(GrVkGpu* gpu) {}
 
     static constexpr uint32_t kMaxInputBuffers = 2;
 
@@ -178,6 +187,10 @@
     VkViewport fCachedViewport;
     VkRect2D   fCachedScissor;
     float      fCachedBlendConstant[4];
+
+#ifdef SK_DEBUG
+    mutable bool fResourcesReleased = false;
+#endif
 };
 
 class GrVkSecondaryCommandBuffer;
@@ -186,10 +199,10 @@
 public:
     ~GrVkPrimaryCommandBuffer() override;
 
-    static GrVkPrimaryCommandBuffer* Create(const GrVkGpu* gpu, VkCommandPool cmdPool);
+    static GrVkPrimaryCommandBuffer* Create(const GrVkGpu* gpu, GrVkCommandPool* cmdPool);
 
     void begin(const GrVkGpu* gpu);
-    void end(const GrVkGpu* gpu);
+    void end(GrVkGpu* gpu);
 
     // Begins render pass on this command buffer. The framebuffer from GrVkRenderTarget will be used
     // in the render pass.
@@ -283,6 +296,8 @@
                        SkTArray<GrVkSemaphore::Resource*>& waitSemaphores);
     bool finished(const GrVkGpu* gpu) const;
 
+    void recycleSecondaryCommandBuffers();
+
 #ifdef SK_TRACE_VK_RESOURCES
     void dumpInfo() const override {
         SkDebugf("GrVkPrimaryCommandBuffer: %d (%d refs)\n", fCmdBuffer, this->getRefCnt());
@@ -290,13 +305,15 @@
 #endif
 
 private:
-    explicit GrVkPrimaryCommandBuffer(VkCommandBuffer cmdBuffer)
-        : INHERITED(cmdBuffer)
+    explicit GrVkPrimaryCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool)
+        : INHERITED(cmdBuffer, cmdPool)
         , fSubmitFence(VK_NULL_HANDLE) {}
 
-    void onFreeGPUData(const GrVkGpu* gpu) const override;
+    void onFreeGPUData(GrVkGpu* gpu) const override;
 
-    void onReset(GrVkGpu* gpu) override;
+    void onAbandonGPUData() const override;
+
+    void onReleaseResources(GrVkGpu* gpu) override;
 
     SkTArray<GrVkSecondaryCommandBuffer*, true> fSecondaryCommandBuffers;
     VkFence                                     fSubmitFence;
@@ -306,11 +323,11 @@
 
 class GrVkSecondaryCommandBuffer : public GrVkCommandBuffer {
 public:
-    static GrVkSecondaryCommandBuffer* Create(const GrVkGpu* gpu, VkCommandPool cmdPool);
+    static GrVkSecondaryCommandBuffer* Create(const GrVkGpu* gpu, GrVkCommandPool* cmdPool);
 
     void begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
                const GrVkRenderPass* compatibleRenderPass);
-    void end(const GrVkGpu* gpu);
+    void end(GrVkGpu* gpu);
 
     VkCommandBuffer vkCommandBuffer() { return fCmdBuffer; }
 
@@ -321,11 +338,12 @@
 #endif
 
 private:
-    explicit GrVkSecondaryCommandBuffer(VkCommandBuffer cmdBuffer)
-        : INHERITED(cmdBuffer) {
-    }
+    explicit GrVkSecondaryCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool)
+        : INHERITED(cmdBuffer, cmdPool) {}
 
-    void onFreeGPUData(const GrVkGpu* gpu) const override {}
+    void onFreeGPUData(GrVkGpu* gpu) const override {}
+
+    void onAbandonGPUData() const override {}
 
     friend class GrVkPrimaryCommandBuffer;
 
diff --git a/src/gpu/vk/GrVkCommandPool.cpp b/src/gpu/vk/GrVkCommandPool.cpp
new file mode 100644
index 0000000..9d7c17e
--- /dev/null
+++ b/src/gpu/vk/GrVkCommandPool.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrVkCommandPool.h"
+
+#include "GrContextPriv.h"
+#include "GrVkCommandBuffer.h"
+#include "GrVkGpu.h"
+
+GrVkCommandPool* GrVkCommandPool::Create(const GrVkGpu* gpu) {
+    const VkCommandPoolCreateInfo cmdPoolInfo = {
+        VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,      // sType
+        nullptr,                                         // pNext
+        VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
+        VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, // CmdPoolCreateFlags
+        gpu->queueIndex(),                              // queueFamilyIndex
+    };
+    VkCommandPool pool;
+    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateCommandPool(gpu->device(), &cmdPoolInfo,
+                                                               nullptr, &pool));
+    return new GrVkCommandPool(gpu, pool);
+}
+
+GrVkCommandPool::GrVkCommandPool(const GrVkGpu* gpu, VkCommandPool commandPool)
+        : fCommandPool(commandPool) {
+    fPrimaryCommandBuffer = GrVkPrimaryCommandBuffer::Create(gpu, this);
+}
+
+GrVkSecondaryCommandBuffer* GrVkCommandPool::findOrCreateSecondaryCommandBuffer(GrVkGpu* gpu) {
+    if (fAvailableSecondaryBuffers.count()) {
+        GrVkSecondaryCommandBuffer* result = fAvailableSecondaryBuffers.back();
+        fAvailableSecondaryBuffers.pop_back();
+        return result;
+    }
+    return GrVkSecondaryCommandBuffer::Create(gpu, this);
+}
+
+void GrVkCommandPool::recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* buffer) {
+    SkASSERT(buffer->commandPool() == this);
+    fAvailableSecondaryBuffers.push_back(buffer);
+}
+
+void GrVkCommandPool::close() {
+    fOpen = false;
+}
+
+void GrVkCommandPool::reset(GrVkGpu* gpu) {
+    SkASSERT(!fOpen);
+    fOpen = true;
+    fPrimaryCommandBuffer->recycleSecondaryCommandBuffers();
+    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), ResetCommandPool(gpu->device(), fCommandPool, 0));
+}
+
+void GrVkCommandPool::releaseResources(GrVkGpu* gpu) {
+    SkASSERT(!fOpen);
+    fPrimaryCommandBuffer->releaseResources(gpu);
+    for (GrVkSecondaryCommandBuffer* buffer : fAvailableSecondaryBuffers) {
+        buffer->releaseResources(gpu);
+    }
+}
+
+void GrVkCommandPool::abandonGPUData() const {
+    fPrimaryCommandBuffer->unrefAndAbandon();
+    for (GrVkSecondaryCommandBuffer* buffer : fAvailableSecondaryBuffers) {
+        SkASSERT(buffer->unique());
+        buffer->unrefAndAbandon();
+    }
+}
+
+void GrVkCommandPool::freeGPUData(GrVkGpu* gpu) const {
+    fPrimaryCommandBuffer->unref(gpu);
+    for (GrVkSecondaryCommandBuffer* buffer : fAvailableSecondaryBuffers) {
+        SkASSERT(buffer->unique());
+        buffer->unref(gpu);
+    }
+    if (fCommandPool != VK_NULL_HANDLE) {
+        GR_VK_CALL(gpu->vkInterface(),
+                   DestroyCommandPool(gpu->device(), fCommandPool, nullptr));
+    }
+}
diff --git a/src/gpu/vk/GrVkCommandPool.h b/src/gpu/vk/GrVkCommandPool.h
new file mode 100644
index 0000000..f65acfc
--- /dev/null
+++ b/src/gpu/vk/GrVkCommandPool.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrVkCommandPool_DEFINED
+#define GrVkCommandPool_DEFINED
+
+#include "GrVkGpuCommandBuffer.h"
+#include "GrVkInterface.h"
+#include "GrVkResource.h"
+#include "GrVkResourceProvider.h"
+
+class GrVkPrimaryCommandBuffer;
+class GrVkSecondaryCommandBuffer;
+class GrVkGpu;
+
+class GrVkCommandPool : public GrVkResource {
+public:
+    static GrVkCommandPool* Create(const GrVkGpu* gpu);
+
+    VkCommandPool vkCommandPool() const {
+        return fCommandPool;
+    }
+
+    void reset(GrVkGpu* gpu);
+
+    void releaseResources(GrVkGpu* gpu);
+
+    GrVkPrimaryCommandBuffer* getPrimaryCommandBuffer() { return fPrimaryCommandBuffer; }
+
+    GrVkSecondaryCommandBuffer* findOrCreateSecondaryCommandBuffer(GrVkGpu* gpu);
+
+    void recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* buffer);
+
+    // marks that we are finished with this command pool; it is not legal to continue creating or
+    // writing to command buffers in a closed pool
+    void close();
+
+    // returns true if close() has not been called
+    bool isOpen() const { return fOpen; }
+
+#ifdef SK_DEBUG
+    void dumpInfo() const override {
+        SkDebugf("GrVkCommandPool: %p (%d refs)\n", fCommandPool, this->getRefCnt());
+    }
+#endif
+
+private:
+    GrVkCommandPool() = delete;
+
+    GrVkCommandPool(const GrVkGpu* gpu, VkCommandPool commandPool);
+
+    void abandonGPUData() const override;
+
+    void freeGPUData(GrVkGpu* gpu) const override;
+
+    bool fOpen = true;
+
+    VkCommandPool fCommandPool;
+
+    GrVkPrimaryCommandBuffer* fPrimaryCommandBuffer;
+
+    // Array of available secondary command buffers that are not in flight
+    SkSTArray<4, GrVkSecondaryCommandBuffer*, true> fAvailableSecondaryBuffers;
+};
+
+#endif
diff --git a/src/gpu/vk/GrVkCopyManager.cpp b/src/gpu/vk/GrVkCopyManager.cpp
index efe86b3..c3942fe 100644
--- a/src/gpu/vk/GrVkCopyManager.cpp
+++ b/src/gpu/vk/GrVkCopyManager.cpp
@@ -13,6 +13,7 @@
 #include "GrSurface.h"
 #include "GrTexturePriv.h"
 #include "GrVkCommandBuffer.h"
+#include "GrVkCommandPool.h"
 #include "GrVkCopyPipeline.h"
 #include "GrVkDescriptorSet.h"
 #include "GrVkGpu.h"
@@ -351,8 +352,7 @@
     GrVkPrimaryCommandBuffer* cmdBuffer = gpu->currentCommandBuffer();
     cmdBuffer->beginRenderPass(gpu, renderPass, nullptr, *rt, bounds, true);
 
-    GrVkSecondaryCommandBuffer* secondary =
-                                       gpu->resourceProvider().findOrCreateSecondaryCommandBuffer();
+    GrVkSecondaryCommandBuffer* secondary = gpu->cmdPool()->findOrCreateSecondaryCommandBuffer(gpu);
     if (!secondary) {
         return false;
     }
diff --git a/src/gpu/vk/GrVkDescriptorPool.cpp b/src/gpu/vk/GrVkDescriptorPool.cpp
index b89145a..ffb6626 100644
--- a/src/gpu/vk/GrVkDescriptorPool.cpp
+++ b/src/gpu/vk/GrVkDescriptorPool.cpp
@@ -44,7 +44,7 @@
     GR_VK_CALL_ERRCHECK(gpu->vkInterface(), ResetDescriptorPool(gpu->device(), fDescPool, 0));
 }
 
-void GrVkDescriptorPool::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkDescriptorPool::freeGPUData(GrVkGpu* gpu) const {
     // Destroying the VkDescriptorPool will automatically free and delete any VkDescriptorSets
     // allocated from the pool.
     GR_VK_CALL(gpu->vkInterface(), DestroyDescriptorPool(gpu->device(), fDescPool, nullptr));
diff --git a/src/gpu/vk/GrVkDescriptorPool.h b/src/gpu/vk/GrVkDescriptorPool.h
index 8d77c20..7abf645 100644
--- a/src/gpu/vk/GrVkDescriptorPool.h
+++ b/src/gpu/vk/GrVkDescriptorPool.h
@@ -39,7 +39,7 @@
 #endif
 
 private:
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
 
     VkDescriptorType     fType;
     uint32_t             fCount;
diff --git a/src/gpu/vk/GrVkDescriptorSet.cpp b/src/gpu/vk/GrVkDescriptorSet.cpp
index 3cb1035..4a662aa 100644
--- a/src/gpu/vk/GrVkDescriptorSet.cpp
+++ b/src/gpu/vk/GrVkDescriptorSet.cpp
@@ -20,7 +20,7 @@
     fPool->ref();
 }
 
-void GrVkDescriptorSet::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkDescriptorSet::freeGPUData(GrVkGpu* gpu) const {
     fPool->unref(gpu);
 }
 
diff --git a/src/gpu/vk/GrVkDescriptorSet.h b/src/gpu/vk/GrVkDescriptorSet.h
index 56cfb45..cb586e3 100644
--- a/src/gpu/vk/GrVkDescriptorSet.h
+++ b/src/gpu/vk/GrVkDescriptorSet.h
@@ -33,7 +33,7 @@
 #endif
 
 private:
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
     void abandonGPUData() const override;
     void onRecycle(GrVkGpu* gpu) const override;
 
diff --git a/src/gpu/vk/GrVkDescriptorSetManager.cpp b/src/gpu/vk/GrVkDescriptorSetManager.cpp
index ba0a060..84a2055 100644
--- a/src/gpu/vk/GrVkDescriptorSetManager.cpp
+++ b/src/gpu/vk/GrVkDescriptorSetManager.cpp
@@ -97,7 +97,7 @@
     fFreeSets.push_back(descSet);
 }
 
-void GrVkDescriptorSetManager::release(const GrVkGpu* gpu) {
+void GrVkDescriptorSetManager::release(GrVkGpu* gpu) {
     fPoolManager.freeGPUResources(gpu);
 
     for (int i = 0; i < fFreeSets.count(); ++i) {
@@ -310,7 +310,7 @@
                                                                    ds));
 }
 
-void GrVkDescriptorSetManager::DescriptorPoolManager::freeGPUResources(const GrVkGpu* gpu) {
+void GrVkDescriptorSetManager::DescriptorPoolManager::freeGPUResources(GrVkGpu* gpu) {
     if (fDescLayout) {
         GR_VK_CALL(gpu->vkInterface(), DestroyDescriptorSetLayout(gpu->device(), fDescLayout,
                                                                   nullptr));
diff --git a/src/gpu/vk/GrVkDescriptorSetManager.h b/src/gpu/vk/GrVkDescriptorSetManager.h
index f9ee7b2..64bde8f 100644
--- a/src/gpu/vk/GrVkDescriptorSetManager.h
+++ b/src/gpu/vk/GrVkDescriptorSetManager.h
@@ -37,7 +37,7 @@
     ~GrVkDescriptorSetManager() {}
 
     void abandon();
-    void release(const GrVkGpu* gpu);
+    void release(GrVkGpu* gpu);
 
     VkDescriptorSetLayout layout() const { return fPoolManager.fDescLayout; }
 
@@ -63,7 +63,7 @@
 
         void getNewDescriptorSet(GrVkGpu* gpu, VkDescriptorSet* ds);
 
-        void freeGPUResources(const GrVkGpu* gpu);
+        void freeGPUResources(GrVkGpu* gpu);
         void abandonGPUResources();
 
         VkDescriptorSetLayout  fDescLayout;
diff --git a/src/gpu/vk/GrVkFramebuffer.cpp b/src/gpu/vk/GrVkFramebuffer.cpp
index f9add63..11d10f95 100644
--- a/src/gpu/vk/GrVkFramebuffer.cpp
+++ b/src/gpu/vk/GrVkFramebuffer.cpp
@@ -51,7 +51,7 @@
     return new GrVkFramebuffer(framebuffer);
 }
 
-void GrVkFramebuffer::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkFramebuffer::freeGPUData(GrVkGpu* gpu) const {
     SkASSERT(fFramebuffer);
     GR_VK_CALL(gpu->vkInterface(), DestroyFramebuffer(gpu->device(), fFramebuffer, nullptr));
 }
diff --git a/src/gpu/vk/GrVkFramebuffer.h b/src/gpu/vk/GrVkFramebuffer.h
index b817c7a..9885f0b 100644
--- a/src/gpu/vk/GrVkFramebuffer.h
+++ b/src/gpu/vk/GrVkFramebuffer.h
@@ -39,7 +39,7 @@
     GrVkFramebuffer(const GrVkFramebuffer&);
     GrVkFramebuffer& operator=(const GrVkFramebuffer&);
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
 
     VkFramebuffer  fFramebuffer;
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 833d1c0..86e1f04 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkGpu.h"
 
+#include "GrContextPriv.h"
 #include "GrBackendSemaphore.h"
 #include "GrBackendSurface.h"
 #include "GrContextOptions.h"
@@ -18,6 +19,7 @@
 #include "GrTexturePriv.h"
 #include "GrVkAMDMemoryAllocator.h"
 #include "GrVkCommandBuffer.h"
+#include "GrVkCommandPool.h"
 #include "GrVkGpuCommandBuffer.h"
 #include "GrVkImage.h"
 #include "GrVkIndexBuffer.h"
@@ -168,31 +170,20 @@
     VK_CALL(GetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &fPhysDevProps));
     VK_CALL(GetPhysicalDeviceMemoryProperties(backendContext.fPhysicalDevice, &fPhysDevMemProps));
 
-    const VkCommandPoolCreateInfo cmdPoolInfo = {
-        VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,      // sType
-        nullptr,                                         // pNext
-        VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
-        VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, // CmdPoolCreateFlags
-        backendContext.fGraphicsQueueIndex,              // queueFamilyIndex
-    };
-    GR_VK_CALL_ERRCHECK(this->vkInterface(), CreateCommandPool(fDevice, &cmdPoolInfo, nullptr,
-                                                               &fCmdPool));
-
-    // must call this after creating the CommandPool
-    fResourceProvider.init();
-    fCurrentCmdBuffer = fResourceProvider.findOrCreatePrimaryCommandBuffer();
+    fCmdPool = fResourceProvider.findOrCreateCommandPool();
+    fCurrentCmdBuffer = fCmdPool->getPrimaryCommandBuffer();
     SkASSERT(fCurrentCmdBuffer);
+    fResourceProvider.init();
     fCurrentCmdBuffer->begin(this);
 }
 
 void GrVkGpu::destroyResources() {
-    if (fCurrentCmdBuffer) {
-        fCurrentCmdBuffer->end(this);
-        fCurrentCmdBuffer->unref(this);
+    if (fCmdPool) {
+        fCmdPool->getPrimaryCommandBuffer()->end(this);
+        fCmdPool->close();
     }
 
     // wait for all commands to finish
-    fResourceProvider.checkCommandBuffers();
     VkResult res = VK_CALL(QueueWaitIdle(fQueue));
 
     // On windows, sometimes calls to QueueWaitIdle return before actually signalling the fences
@@ -213,6 +204,11 @@
     SkASSERT(VK_SUCCESS == res || VK_ERROR_DEVICE_LOST == res);
 #endif
 
+    if (fCmdPool) {
+        fCmdPool->unref(this);
+        fCmdPool = nullptr;
+    }
+
     for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) {
         fSemaphoresToWaitOn[i]->unref(this);
     }
@@ -229,10 +225,6 @@
     // must call this just before we destroy the command pool and VkDevice
     fResourceProvider.destroyResources(VK_ERROR_DEVICE_LOST == res);
 
-    if (fCmdPool != VK_NULL_HANDLE) {
-        VK_CALL(DestroyCommandPool(fDevice, fCmdPool, nullptr));
-    }
-
     fMemoryAllocator.reset();
 
     fQueue = VK_NULL_HANDLE;
@@ -254,8 +246,9 @@
         if (DisconnectType::kCleanup == type) {
             this->destroyResources();
         } else {
-            if (fCurrentCmdBuffer) {
-                fCurrentCmdBuffer->unrefAndAbandon();
+            if (fCmdPool) {
+                fCmdPool->unrefAndAbandon();
+                fCmdPool = nullptr;
             }
             for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) {
                 fSemaphoresToWaitOn[i]->unrefAndAbandon();
@@ -273,7 +266,6 @@
         fSemaphoresToWaitOn.reset();
         fSemaphoresToSignal.reset();
         fCurrentCmdBuffer = nullptr;
-        fCmdPool = VK_NULL_HANDLE;
         fDisconnected = true;
     }
 }
@@ -304,7 +296,7 @@
 void GrVkGpu::submitCommandBuffer(SyncQueue sync) {
     SkASSERT(fCurrentCmdBuffer);
     fCurrentCmdBuffer->end(this);
-
+    fCmdPool->close();
     fCurrentCmdBuffer->submitToQueue(this, fQueue, sync, fSemaphoresToSignal, fSemaphoresToWaitOn);
 
     // We must delete and drawables that have been waitint till submit for us to destroy.
@@ -319,13 +311,11 @@
     }
     fSemaphoresToSignal.reset();
 
+    // Release old command pool and create a new one
+    fCmdPool->unref(this);
     fResourceProvider.checkCommandBuffers();
-
-    // Release old command buffer and create a new one
-    fCurrentCmdBuffer->unref(this);
-    fCurrentCmdBuffer = fResourceProvider.findOrCreatePrimaryCommandBuffer();
-    SkASSERT(fCurrentCmdBuffer);
-
+    fCmdPool = fResourceProvider.findOrCreateCommandPool();
+    fCurrentCmdBuffer = fCmdPool->getPrimaryCommandBuffer();
     fCurrentCmdBuffer->begin(this);
 }
 
@@ -1221,7 +1211,7 @@
     const VkCommandBufferAllocateInfo cmdInfo = {
         VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
         nullptr,                                          // pNext
-        fCmdPool,                                         // commandPool
+        fCmdPool->vkCommandPool(),                        // commandPool
         VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
         1                                                 // bufferCount
     };
@@ -1290,7 +1280,7 @@
         GrVkMemory::FreeImageMemory(this, false, alloc);
         VK_CALL(DestroyImage(fDevice, image, nullptr));
         VK_CALL(EndCommandBuffer(cmdBuffer));
-        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
+        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
         return false;
     }
 
@@ -1300,7 +1290,7 @@
         VK_CALL(DestroyImage(fDevice, image, nullptr));
         VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
         VK_CALL(EndCommandBuffer(cmdBuffer));
-        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
+        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
         return false;
     }
 
@@ -1317,7 +1307,7 @@
             GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
             VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
             VK_CALL(EndCommandBuffer(cmdBuffer));
-            VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
+            VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
             return false;
         }
         currentWidth = SkTMax(1, currentWidth / 2);
@@ -1421,7 +1411,7 @@
         VK_CALL(DestroyImage(fDevice, image, nullptr));
         GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
         VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
-        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
+        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
         VK_CALL(DestroyFence(fDevice, fence, nullptr));
         SkDebugf("Fence failed to signal: %d\n", err);
         SK_ABORT("failing");
@@ -1433,7 +1423,7 @@
         GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
         VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
     }
-    VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
+    VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
     VK_CALL(DestroyFence(fDevice, fence, nullptr));
 
     info->fImage = image;
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 1ec917f6..5aff1ff 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -24,6 +24,7 @@
 class GrPipeline;
 
 class GrVkBufferImpl;
+class GrVkCommandPool;
 class GrVkGpuRTCommandBuffer;
 class GrVkGpuTextureCommandBuffer;
 class GrVkMemoryAllocator;
@@ -56,7 +57,7 @@
     VkDevice device() const { return fDevice; }
     VkQueue  queue() const { return fQueue; }
     uint32_t  queueIndex() const { return fQueueIndex; }
-    VkCommandPool cmdPool() const { return fCmdPool; }
+    GrVkCommandPool* cmdPool() const { return fCmdPool; }
     VkPhysicalDeviceProperties physicalDeviceProperties() const {
         return fPhysDevProps;
     }
@@ -259,8 +260,10 @@
 
     // Created by GrVkGpu
     GrVkResourceProvider                                  fResourceProvider;
-    VkCommandPool                                         fCmdPool;
 
+    GrVkCommandPool*                                      fCmdPool;
+
+    // just a raw pointer; object's lifespan is managed by fCmdPool
     GrVkPrimaryCommandBuffer*                             fCurrentCmdBuffer;
 
     SkSTArray<1, GrVkSemaphore::Resource*>                fSemaphoresToWaitOn;
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 922e410..faeba92 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -15,6 +15,7 @@
 #include "GrRenderTargetPriv.h"
 #include "GrTexturePriv.h"
 #include "GrVkCommandBuffer.h"
+#include "GrVkCommandPool.h"
 #include "GrVkGpu.h"
 #include "GrVkPipeline.h"
 #include "GrVkRenderPass.h"
@@ -122,7 +123,7 @@
         cbInfo.fLoadStoreState = LoadStoreState::kStartsWithDiscard;
     }
 
-    cbInfo.fCommandBuffers.push_back(fGpu->resourceProvider().findOrCreateSecondaryCommandBuffer());
+    cbInfo.fCommandBuffers.push_back(fGpu->cmdPool()->findOrCreateSecondaryCommandBuffer(fGpu));
     cbInfo.currentCmdBuf()->begin(fGpu, vkRT->framebuffer(), cbInfo.fRenderPass);
 }
 
@@ -461,7 +462,7 @@
 
     CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
     cbInfo.currentCmdBuf()->end(fGpu);
-    cbInfo.fCommandBuffers.push_back(fGpu->resourceProvider().findOrCreateSecondaryCommandBuffer());
+    cbInfo.fCommandBuffers.push_back(fGpu->cmdPool()->findOrCreateSecondaryCommandBuffer(fGpu));
     cbInfo.currentCmdBuf()->begin(fGpu, vkRT->framebuffer(), cbInfo.fRenderPass);
 }
 
@@ -491,7 +492,7 @@
     }
     cbInfo.fLoadStoreState = LoadStoreState::kLoadAndStore;
 
-    cbInfo.fCommandBuffers.push_back(fGpu->resourceProvider().findOrCreateSecondaryCommandBuffer());
+    cbInfo.fCommandBuffers.push_back(fGpu->cmdPool()->findOrCreateSecondaryCommandBuffer(fGpu));
     // It shouldn't matter what we set the clear color to here since we will assume loading of the
     // attachment.
     memset(&cbInfo.fColorClearValue, 0, sizeof(VkClearValue));
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index 67c19d0..1da1275 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -216,7 +216,7 @@
     SkASSERT(!fResource);
 }
 
-void GrVkImage::releaseImage(const GrVkGpu* gpu) {
+void GrVkImage::releaseImage(GrVkGpu* gpu) {
     if (fInfo.fCurrentQueueFamily != fInitialQueueFamily) {
         this->setImageLayout(gpu, this->currentLayout(), 0, 0, false, true);
     }
@@ -238,14 +238,14 @@
     fResource->setRelease(std::move(releaseHelper));
 }
 
-void GrVkImage::Resource::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkImage::Resource::freeGPUData(GrVkGpu* gpu) const {
     SkASSERT(!fReleaseHelper);
     VK_CALL(gpu, DestroyImage(gpu->device(), fImage, nullptr));
     bool isLinear = (VK_IMAGE_TILING_LINEAR == fImageTiling);
     GrVkMemory::FreeImageMemory(gpu, isLinear, fAlloc);
 }
 
-void GrVkImage::BorrowedResource::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkImage::BorrowedResource::freeGPUData(GrVkGpu* gpu) const {
     this->invokeReleaseProc();
 }
 
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 44c8ce2..e593c1f 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -115,7 +115,7 @@
     static VkAccessFlags LayoutToSrcAccessMask(const VkImageLayout layout);
 
 protected:
-    void releaseImage(const GrVkGpu* gpu);
+    void releaseImage(GrVkGpu* gpu);
     void abandonImage();
 
     void setNewResource(VkImage image, const GrVkAlloc& alloc, VkImageTiling tiling);
@@ -155,7 +155,7 @@
         mutable sk_sp<GrReleaseProcHelper> fReleaseHelper;
 
     private:
-        void freeGPUData(const GrVkGpu* gpu) const override;
+        void freeGPUData(GrVkGpu* gpu) const override;
         void abandonGPUData() const override {
             SkASSERT(!fReleaseHelper);
         }
@@ -182,7 +182,7 @@
             }
         }
 
-        void freeGPUData(const GrVkGpu* gpu) const override;
+        void freeGPUData(GrVkGpu* gpu) const override;
         void abandonGPUData() const override;
     };
 
diff --git a/src/gpu/vk/GrVkImageView.cpp b/src/gpu/vk/GrVkImageView.cpp
index 342ce46..82c9457 100644
--- a/src/gpu/vk/GrVkImageView.cpp
+++ b/src/gpu/vk/GrVkImageView.cpp
@@ -61,7 +61,7 @@
     return new GrVkImageView(imageView, ycbcrConversion);
 }
 
-void GrVkImageView::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkImageView::freeGPUData(GrVkGpu* gpu) const {
     GR_VK_CALL(gpu->vkInterface(), DestroyImageView(gpu->device(), fImageView, nullptr));
 
     if (fYcbcrConversion) {
diff --git a/src/gpu/vk/GrVkImageView.h b/src/gpu/vk/GrVkImageView.h
index d953249..27d70d2 100644
--- a/src/gpu/vk/GrVkImageView.h
+++ b/src/gpu/vk/GrVkImageView.h
@@ -42,7 +42,7 @@
     GrVkImageView(const GrVkImageView&);
     GrVkImageView& operator=(const GrVkImageView&);
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
     void abandonGPUData() const override;
 
     VkImageView  fImageView;
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 68dea29..90d5e44 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -569,7 +569,7 @@
     return new GrVkPipeline(vkPipeline);
 }
 
-void GrVkPipeline::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkPipeline::freeGPUData(GrVkGpu* gpu) const {
     GR_VK_CALL(gpu->vkInterface(), DestroyPipeline(gpu->device(), fPipeline, nullptr));
 }
 
diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h
index 579ad75..fa164be 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -56,7 +56,7 @@
     VkPipeline  fPipeline;
 
 private:
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
 
     typedef GrVkResource INHERITED;
 };
diff --git a/src/gpu/vk/GrVkPipelineLayout.cpp b/src/gpu/vk/GrVkPipelineLayout.cpp
index 7deb5ac..220e28b 100644
--- a/src/gpu/vk/GrVkPipelineLayout.cpp
+++ b/src/gpu/vk/GrVkPipelineLayout.cpp
@@ -9,6 +9,6 @@
 #include "GrVkGpu.h"
 #include "GrVkUtil.h"
 
-void GrVkPipelineLayout::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkPipelineLayout::freeGPUData(GrVkGpu* gpu) const {
     GR_VK_CALL(gpu->vkInterface(), DestroyPipelineLayout(gpu->device(), fPipelineLayout, nullptr));
 }
diff --git a/src/gpu/vk/GrVkPipelineLayout.h b/src/gpu/vk/GrVkPipelineLayout.h
index 50ebf85..a7b6dc5 100644
--- a/src/gpu/vk/GrVkPipelineLayout.h
+++ b/src/gpu/vk/GrVkPipelineLayout.h
@@ -29,7 +29,7 @@
     GrVkPipelineLayout(const GrVkPipelineLayout&);
     GrVkPipelineLayout& operator=(const GrVkPipelineLayout&);
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
 
     VkPipelineLayout  fPipelineLayout;
 
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 9f18b1a..c639377 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -75,7 +75,7 @@
     SkASSERT(!fPipelineLayout);
 }
 
-void GrVkPipelineState::freeGPUResources(const GrVkGpu* gpu) {
+void GrVkPipelineState::freeGPUResources(GrVkGpu* gpu) {
     if (fPipeline) {
         fPipeline->unref(gpu);
         fPipeline = nullptr;
diff --git a/src/gpu/vk/GrVkPipelineState.h b/src/gpu/vk/GrVkPipelineState.h
index e8a1b39..cd28a82 100644
--- a/src/gpu/vk/GrVkPipelineState.h
+++ b/src/gpu/vk/GrVkPipelineState.h
@@ -71,7 +71,7 @@
 
     void addUniformResources(GrVkCommandBuffer&, GrVkSampler*[], GrVkTexture*[], int numTextures);
 
-    void freeGPUResources(const GrVkGpu* gpu);
+    void freeGPUResources(GrVkGpu* gpu);
 
     void abandonGPUResources();
 
diff --git a/src/gpu/vk/GrVkRenderPass.cpp b/src/gpu/vk/GrVkRenderPass.cpp
index e0daa3f..682244c 100644
--- a/src/gpu/vk/GrVkRenderPass.cpp
+++ b/src/gpu/vk/GrVkRenderPass.cpp
@@ -163,7 +163,7 @@
     this->init(gpu, colorOp, stencilOp);
 }
 
-void GrVkRenderPass::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkRenderPass::freeGPUData(GrVkGpu* gpu) const {
     GR_VK_CALL(gpu->vkInterface(), DestroyRenderPass(gpu->device(), fRenderPass, nullptr));
 }
 
diff --git a/src/gpu/vk/GrVkRenderPass.h b/src/gpu/vk/GrVkRenderPass.h
index d5fa722..ef752b1 100644
--- a/src/gpu/vk/GrVkRenderPass.h
+++ b/src/gpu/vk/GrVkRenderPass.h
@@ -125,7 +125,7 @@
 
     bool isCompatible(const AttachmentsDescriptor&, const AttachmentFlags&) const;
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
 
     VkRenderPass          fRenderPass;
     AttachmentFlags       fAttachmentFlags;
diff --git a/src/gpu/vk/GrVkResource.h b/src/gpu/vk/GrVkResource.h
index 1231996..9cb915a 100644
--- a/src/gpu/vk/GrVkResource.h
+++ b/src/gpu/vk/GrVkResource.h
@@ -55,8 +55,14 @@
             });
             SkASSERT(0 == fHashSet.count());
         }
-        void add(const GrVkResource* r) { fHashSet.add(r); }
-        void remove(const GrVkResource* r) { fHashSet.remove(r); }
+
+        void add(const GrVkResource* r) {
+            fHashSet.add(r);
+        }
+
+        void remove(const GrVkResource* r) {
+            fHashSet.remove(r);
+        }
 
     private:
         SkTHashSet<const GrVkResource*, GrVkResource::Hash> fHashSet;
@@ -103,8 +109,9 @@
         Must be balanced by a call to unref() or unrefAndFreeResources().
      */
     void ref() const {
-        SkASSERT(this->getRefCnt() > 0);
-        (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed);  // No barrier required.
+        // No barrier required.
+        SkDEBUGCODE(int newRefCount = )fRefCnt.fetch_add(+1, std::memory_order_relaxed);
+        SkASSERT(newRefCount >= 1);
     }
 
     /** Decrement the reference count. If the reference count is 1 before the
@@ -112,11 +119,12 @@
         the object needs to have been allocated via new, and not on the stack.
         Any GPU data associated with this resource will be freed before it's deleted.
      */
-    void unref(const GrVkGpu* gpu) const {
-        SkASSERT(this->getRefCnt() > 0);
+    void unref(GrVkGpu* gpu) const {
         SkASSERT(gpu);
         // A release here acts in place of all releases we "should" have been doing in ref().
-        if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
+        int newRefCount = fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
+        SkASSERT(newRefCount >= 0);
+        if (newRefCount == 1) {
             // Like unique(), the acquire is only needed on success, to make sure
             // code in internal_dispose() doesn't happen before the decrement.
             this->internal_dispose(gpu);
@@ -127,7 +135,9 @@
     void unrefAndAbandon() const {
         SkASSERT(this->getRefCnt() > 0);
         // A release here acts in place of all releases we "should" have been doing in ref().
-        if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
+        int newRefCount = fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
+        SkASSERT(newRefCount >= 0);
+        if (newRefCount == 1) {
             // Like unique(), the acquire is only needed on success, to make sure
             // code in internal_dispose() doesn't happen before the decrement.
             this->internal_dispose();
@@ -157,7 +167,7 @@
     /** Must be implemented by any subclasses.
      *  Deletes any Vk data associated with this resource
      */
-    virtual void freeGPUData(const GrVkGpu* gpu) const = 0;
+    virtual void freeGPUData(GrVkGpu* gpu) const = 0;
 
     /**
      * Called from unrefAndAbandon. Resources should do any necessary cleanup without freeing
@@ -169,7 +179,7 @@
     /**
      *  Called when the ref count goes to 0. Will free Vk resources.
      */
-    void internal_dispose(const GrVkGpu* gpu) const {
+    void internal_dispose(GrVkGpu* gpu) const {
         this->freeGPUData(gpu);
 #ifdef SK_TRACE_VK_RESOURCES
         GetTrace()->remove(this);
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 75a459a..2b54878 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -7,14 +7,17 @@
 
 #include "GrVkResourceProvider.h"
 
+#include "GrContextPriv.h"
 #include "GrSamplerState.h"
 #include "GrVkCommandBuffer.h"
+#include "GrVkCommandPool.h"
 #include "GrVkCopyPipeline.h"
 #include "GrVkGpu.h"
 #include "GrVkPipeline.h"
 #include "GrVkRenderTarget.h"
 #include "GrVkUniformBuffer.h"
 #include "GrVkUtil.h"
+#include "SkTaskGroup.h"
 
 #ifdef SK_TRACE_VK_RESOURCES
 std::atomic<uint32_t> GrVkResource::fKeyCounter{0};
@@ -274,49 +277,42 @@
     fDescriptorSetManagers[managerIdx]->recycleDescriptorSet(descSet);
 }
 
-GrVkPrimaryCommandBuffer* GrVkResourceProvider::findOrCreatePrimaryCommandBuffer() {
-    GrVkPrimaryCommandBuffer* cmdBuffer = nullptr;
-    int count = fAvailableCommandBuffers.count();
-    if (count > 0) {
-        cmdBuffer = fAvailableCommandBuffers[count - 1];
-        SkASSERT(cmdBuffer->finished(fGpu));
-        fAvailableCommandBuffers.removeShuffle(count - 1);
+GrVkCommandPool* GrVkResourceProvider::findOrCreateCommandPool() {
+    std::unique_lock<std::recursive_mutex> lock(fBackgroundMutex);
+    GrVkCommandPool* result;
+    if (fAvailableCommandPools.count()) {
+        result = fAvailableCommandPools.back();
+        fAvailableCommandPools.pop_back();
     } else {
-        cmdBuffer = GrVkPrimaryCommandBuffer::Create(fGpu, fGpu->cmdPool());
+        result = GrVkCommandPool::Create(fGpu);
     }
-    fActiveCommandBuffers.push_back(cmdBuffer);
-    cmdBuffer->ref();
-    return cmdBuffer;
+    SkASSERT(result->unique());
+    SkDEBUGCODE(
+        for (const GrVkCommandPool* pool : fActiveCommandPools) {
+            SkASSERT(pool != result);
+        }
+        for (const GrVkCommandPool* pool : fAvailableCommandPools) {
+            SkASSERT(pool != result);
+        }
+    );
+    fActiveCommandPools.push_back(result);
+    result->ref();
+    return result;
 }
 
 void GrVkResourceProvider::checkCommandBuffers() {
-    for (int i = fActiveCommandBuffers.count()-1; i >= 0; --i) {
-        if (fActiveCommandBuffers[i]->finished(fGpu)) {
-            GrVkPrimaryCommandBuffer* cmdBuffer = fActiveCommandBuffers[i];
-            cmdBuffer->reset(fGpu);
-            fAvailableCommandBuffers.push_back(cmdBuffer);
-            fActiveCommandBuffers.removeShuffle(i);
+    for (int i = fActiveCommandPools.count() - 1; i >= 0; --i) {
+        GrVkCommandPool* pool = fActiveCommandPools[i];
+        if (!pool->isOpen()) {
+            GrVkPrimaryCommandBuffer* buffer = pool->getPrimaryCommandBuffer();
+            if (buffer->finished(fGpu)) {
+                fActiveCommandPools.removeShuffle(i);
+                this->backgroundReset(pool);
+            }
         }
     }
 }
 
-GrVkSecondaryCommandBuffer* GrVkResourceProvider::findOrCreateSecondaryCommandBuffer() {
-    GrVkSecondaryCommandBuffer* cmdBuffer = nullptr;
-    int count = fAvailableSecondaryCommandBuffers.count();
-    if (count > 0) {
-        cmdBuffer = fAvailableSecondaryCommandBuffers[count-1];
-        fAvailableSecondaryCommandBuffers.removeShuffle(count - 1);
-    } else {
-        cmdBuffer = GrVkSecondaryCommandBuffer::Create(fGpu, fGpu->cmdPool());
-    }
-    return cmdBuffer;
-}
-
-void GrVkResourceProvider::recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* cb) {
-    cb->reset(fGpu);
-    fAvailableSecondaryCommandBuffers.push_back(cb);
-}
-
 const GrVkResource* GrVkResourceProvider::findOrCreateStandardUniformBufferResource() {
     const GrVkResource* resource = nullptr;
     int count = fAvailableUniformBufferResources.count();
@@ -334,29 +330,6 @@
 }
 
 void GrVkResourceProvider::destroyResources(bool deviceLost) {
-    // release our active command buffers
-    for (int i = 0; i < fActiveCommandBuffers.count(); ++i) {
-        SkASSERT(deviceLost || fActiveCommandBuffers[i]->finished(fGpu));
-        SkASSERT(fActiveCommandBuffers[i]->unique());
-        fActiveCommandBuffers[i]->reset(fGpu);
-        fActiveCommandBuffers[i]->unref(fGpu);
-    }
-    fActiveCommandBuffers.reset();
-    // release our available command buffers
-    for (int i = 0; i < fAvailableCommandBuffers.count(); ++i) {
-        SkASSERT(deviceLost || fAvailableCommandBuffers[i]->finished(fGpu));
-        SkASSERT(fAvailableCommandBuffers[i]->unique());
-        fAvailableCommandBuffers[i]->unref(fGpu);
-    }
-    fAvailableCommandBuffers.reset();
-
-    // release our available secondary command buffers
-    for (int i = 0; i < fAvailableSecondaryCommandBuffers.count(); ++i) {
-        SkASSERT(fAvailableSecondaryCommandBuffers[i]->unique());
-        fAvailableSecondaryCommandBuffers[i]->unref(fGpu);
-    }
-    fAvailableSecondaryCommandBuffers.reset();
-
     // Release all copy pipelines
     for (int i = 0; i < fCopyPipelines.count(); ++i) {
         fCopyPipelines[i]->unref(fGpu);
@@ -380,6 +353,18 @@
     GR_VK_CALL(fGpu->vkInterface(), DestroyPipelineCache(fGpu->device(), fPipelineCache, nullptr));
     fPipelineCache = VK_NULL_HANDLE;
 
+    for (GrVkCommandPool* pool : fActiveCommandPools) {
+        SkASSERT(pool->unique());
+        pool->unref(fGpu);
+    }
+    fActiveCommandPools.reset();
+
+    for (GrVkCommandPool* pool : fAvailableCommandPools) {
+        SkASSERT(pool->unique());
+        pool->unref(fGpu);
+    }
+    fAvailableCommandPools.reset();
+
     // We must release/destroy all command buffers and pipeline states before releasing the
     // GrVkDescriptorSetManagers
     for (int i = 0; i < fDescriptorSetManagers.count(); ++i) {
@@ -396,25 +381,17 @@
 }
 
 void GrVkResourceProvider::abandonResources() {
-    // release our active command buffers
-    for (int i = 0; i < fActiveCommandBuffers.count(); ++i) {
-        SkASSERT(fActiveCommandBuffers[i]->unique());
-        fActiveCommandBuffers[i]->unrefAndAbandon();
+    // Abandon all command pools
+    for (int i = 0; i < fActiveCommandPools.count(); ++i) {
+        SkASSERT(fActiveCommandPools[i]->unique());
+        fActiveCommandPools[i]->unrefAndAbandon();
     }
-    fActiveCommandBuffers.reset();
-    // release our available command buffers
-    for (int i = 0; i < fAvailableCommandBuffers.count(); ++i) {
-        SkASSERT(fAvailableCommandBuffers[i]->unique());
-        fAvailableCommandBuffers[i]->unrefAndAbandon();
+    fActiveCommandPools.reset();
+    for (int i = 0; i < fAvailableCommandPools.count(); ++i) {
+        SkASSERT(fAvailableCommandPools[i]->unique());
+        fAvailableCommandPools[i]->unrefAndAbandon();
     }
-    fAvailableCommandBuffers.reset();
-
-    // release our available secondary command buffers
-    for (int i = 0; i < fAvailableSecondaryCommandBuffers.count(); ++i) {
-        SkASSERT(fAvailableSecondaryCommandBuffers[i]->unique());
-        fAvailableSecondaryCommandBuffers[i]->unrefAndAbandon();
-    }
-    fAvailableSecondaryCommandBuffers.reset();
+    fAvailableCommandPools.reset();
 
     // Abandon all copy pipelines
     for (int i = 0; i < fCopyPipelines.count(); ++i) {
@@ -453,6 +430,26 @@
     fAvailableUniformBufferResources.reset();
 }
 
+void GrVkResourceProvider::backgroundReset(GrVkCommandPool* pool) {
+    SkASSERT(pool->unique());
+    pool->releaseResources(fGpu);
+    SkTaskGroup* taskGroup = fGpu->getContext()->contextPriv().getTaskGroup();
+    if (taskGroup) {
+        taskGroup->add([this, pool]() {
+            this->reset(pool);
+        });
+    } else {
+        this->reset(pool);
+    }
+}
+
+void GrVkResourceProvider::reset(GrVkCommandPool* pool) {
+    SkASSERT(pool->unique());
+    pool->reset(fGpu);
+    std::unique_lock<std::recursive_mutex> providerLock(fBackgroundMutex);
+    fAvailableCommandPools.push_back(pool);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 GrVkResourceProvider::CompatibleRenderPassSet::CompatibleRenderPassSet(
@@ -488,7 +485,7 @@
     return renderPass;
 }
 
-void GrVkResourceProvider::CompatibleRenderPassSet::releaseResources(const GrVkGpu* gpu) {
+void GrVkResourceProvider::CompatibleRenderPassSet::releaseResources(GrVkGpu* gpu) {
     for (int i = 0; i < fRenderPasses.count(); ++i) {
         if (fRenderPasses[i]) {
             fRenderPasses[i]->unref(gpu);
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 7771de1..72d0b60 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -24,9 +24,10 @@
 #include "SkTDynamicHash.h"
 #include "SkTInternalLList.h"
 
-class GrPipeline;
-class GrPrimitiveProcessor;
-class GrSamplerState;
+#include <mutex>
+#include <thread>
+
+class GrVkCommandPool;
 class GrVkCopyPipeline;
 class GrVkGpu;
 class GrVkPipeline;
@@ -84,11 +85,9 @@
                                          const GrVkRenderPass::LoadStoreOps& colorOps,
                                          const GrVkRenderPass::LoadStoreOps& stencilOps);
 
-    GrVkPrimaryCommandBuffer* findOrCreatePrimaryCommandBuffer();
-    void checkCommandBuffers();
+    GrVkCommandPool* findOrCreateCommandPool();
 
-    GrVkSecondaryCommandBuffer* findOrCreateSecondaryCommandBuffer();
-    void recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* cb);
+    void checkCommandBuffers();
 
     // Finds or creates a compatible GrVkDescriptorPool for the requested type and count.
     // The refcount is incremented and a pointer returned.
@@ -168,7 +167,12 @@
     // resource usages.
     void abandonResources();
 
+    void backgroundReset(GrVkCommandPool* pool);
+
+    void reset(GrVkCommandPool* pool);
+
 private:
+
 #ifdef SK_DEBUG
 #define GR_PIPELINE_STATE_CACHE_STATS
 #endif
@@ -231,7 +235,7 @@
                                       const GrVkRenderPass::LoadStoreOps& colorOps,
                                       const GrVkRenderPass::LoadStoreOps& stencilOps);
 
-        void releaseResources(const GrVkGpu* gpu);
+        void releaseResources(GrVkGpu* gpu);
         void abandonResources();
 
     private:
@@ -249,13 +253,11 @@
 
     SkSTArray<4, CompatibleRenderPassSet> fRenderPassArray;
 
-    // Array of PrimaryCommandBuffers that are currently in flight
-    SkSTArray<4, GrVkPrimaryCommandBuffer*, true> fActiveCommandBuffers;
-    // Array of available primary command buffers that are not in flight
-    SkSTArray<4, GrVkPrimaryCommandBuffer*, true> fAvailableCommandBuffers;
+    // Array of command pools that we are waiting on
+    SkSTArray<4, GrVkCommandPool*, true> fActiveCommandPools;
 
-    // Array of available secondary command buffers
-    SkSTArray<16, GrVkSecondaryCommandBuffer*, true> fAvailableSecondaryCommandBuffers;
+    // Array of available command pools that are not in flight
+    SkSTArray<4, GrVkCommandPool*, true> fAvailableCommandPools;
 
     // Array of available uniform buffer resources
     SkSTArray<16, const GrVkResource*, true> fAvailableUniformBufferResources;
@@ -273,6 +275,8 @@
     SkSTArray<4, std::unique_ptr<GrVkDescriptorSetManager>> fDescriptorSetManagers;
 
     GrVkDescriptorSetManager::Handle fUniformDSHandle;
+
+    std::recursive_mutex fBackgroundMutex;
 };
 
 #endif
diff --git a/src/gpu/vk/GrVkSampler.cpp b/src/gpu/vk/GrVkSampler.cpp
index 2aa83eb..57bf2d7 100644
--- a/src/gpu/vk/GrVkSampler.cpp
+++ b/src/gpu/vk/GrVkSampler.cpp
@@ -112,7 +112,7 @@
     return new GrVkSampler(sampler, ycbcrConversion, GenerateKey(samplerState, ycbcrInfo));
 }
 
-void GrVkSampler::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkSampler::freeGPUData(GrVkGpu* gpu) const {
     SkASSERT(fSampler);
     GR_VK_CALL(gpu->vkInterface(), DestroySampler(gpu->device(), fSampler, nullptr));
     if (fYcbcrConversion) {
diff --git a/src/gpu/vk/GrVkSampler.h b/src/gpu/vk/GrVkSampler.h
index dce9ef9..874ba78 100644
--- a/src/gpu/vk/GrVkSampler.h
+++ b/src/gpu/vk/GrVkSampler.h
@@ -67,7 +67,7 @@
             , fKey(key)
             , fUniqueID(GenID()) {}
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
     void abandonGPUData() const override;
 
     static uint32_t GenID() {
diff --git a/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp b/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
index bc08ac1..1465d09 100644
--- a/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
+++ b/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
@@ -68,7 +68,7 @@
 #endif
 }
 
-void GrVkSamplerYcbcrConversion::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkSamplerYcbcrConversion::freeGPUData(GrVkGpu* gpu) const {
     SkASSERT(fYcbcrConversion);
     GR_VK_CALL(gpu->vkInterface(), DestroySamplerYcbcrConversion(gpu->device(), fYcbcrConversion,
                                                                  nullptr));
diff --git a/src/gpu/vk/GrVkSamplerYcbcrConversion.h b/src/gpu/vk/GrVkSamplerYcbcrConversion.h
index 91f6480..372876c 100644
--- a/src/gpu/vk/GrVkSamplerYcbcrConversion.h
+++ b/src/gpu/vk/GrVkSamplerYcbcrConversion.h
@@ -62,7 +62,7 @@
             , fYcbcrConversion(ycbcrConversion)
             , fKey(key) {}
 
-    void freeGPUData(const GrVkGpu* gpu) const override;
+    void freeGPUData(GrVkGpu* gpu) const override;
 
     VkSamplerYcbcrConversion fYcbcrConversion;
     Key                      fKey;
diff --git a/src/gpu/vk/GrVkSemaphore.cpp b/src/gpu/vk/GrVkSemaphore.cpp
index ef53b30..8491782 100644
--- a/src/gpu/vk/GrVkSemaphore.cpp
+++ b/src/gpu/vk/GrVkSemaphore.cpp
@@ -51,7 +51,7 @@
 
 void GrVkSemaphore::onRelease() {
     if (fResource) {
-        fResource->unref(static_cast<const GrVkGpu*>(this->getGpu()));
+        fResource->unref(static_cast<GrVkGpu*>(this->getGpu()));
         fResource = nullptr;
     }
     INHERITED::onRelease();
@@ -65,7 +65,7 @@
     INHERITED::onAbandon();
 }
 
-void GrVkSemaphore::Resource::freeGPUData(const GrVkGpu* gpu) const {
+void GrVkSemaphore::Resource::freeGPUData(GrVkGpu* gpu) const {
     if (fIsOwned) {
         GR_VK_CALL(gpu->vkInterface(),
                    DestroySemaphore(gpu->device(), fSemaphore, nullptr));
diff --git a/src/gpu/vk/GrVkSemaphore.h b/src/gpu/vk/GrVkSemaphore.h
index 6873559..3373ff8 100644
--- a/src/gpu/vk/GrVkSemaphore.h
+++ b/src/gpu/vk/GrVkSemaphore.h
@@ -71,7 +71,7 @@
         }
 #endif
     private:
-        void freeGPUData(const GrVkGpu* gpu) const override;
+        void freeGPUData(GrVkGpu* gpu) const override;
 
         static SkMutex* GetMutex() {
             static SkMutex kMutex;
diff --git a/tests/SurfaceSemaphoreTest.cpp b/tests/SurfaceSemaphoreTest.cpp
index b5e1982..66237fd 100644
--- a/tests/SurfaceSemaphoreTest.cpp
+++ b/tests/SurfaceSemaphoreTest.cpp
@@ -20,6 +20,7 @@
 #include "gl/GrGLUtil.h"
 
 #ifdef SK_VULKAN
+#include "vk/GrVkCommandPool.h"
 #include "vk/GrVkGpu.h"
 #include "vk/GrVkTypes.h"
 #include "vk/GrVkUtil.h"
@@ -245,14 +246,14 @@
         const GrVkInterface* interface = gpu->vkInterface();
         VkDevice device = gpu->device();
         VkQueue queue = gpu->queue();
-        VkCommandPool cmdPool = gpu->cmdPool();
+        GrVkCommandPool* cmdPool = gpu->cmdPool();
         VkCommandBuffer cmdBuffer;
 
         // Create Command Buffer
         const VkCommandBufferAllocateInfo cmdInfo = {
             VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
             nullptr,                                          // pNext
-            cmdPool,                                          // commandPool
+            cmdPool->vkCommandPool(),                         // commandPool
             VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
             1                                                 // bufferCount
         };