Add initial support for creating a vulkan secondary command buffer drawing context.

This sets up the context and adds support for creating RTContexts, RTProxies, RTs,
and GrVkRenderPass's that wrap the external secondary command buffer.

Bug: skia:
Change-Id: I80ebbb690a5fe464f775c5fcad651dfe2a150418
Reviewed-on: https://skia-review.googlesource.com/c/178926
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 86e1f04..7d71d22 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -990,6 +990,36 @@
     return GrVkRenderTarget::MakeWrappedRenderTarget(this, desc, imageInfo, std::move(layout));
 }
 
+sk_sp<GrRenderTarget> GrVkGpu::onWrapVulkanSecondaryCBAsRenderTarget(
+        const SkImageInfo& imageInfo, const GrVkDrawableInfo& vkInfo) {
+    int maxSize = this->caps()->maxTextureSize();
+    if (imageInfo.width() > maxSize || imageInfo.height() > maxSize) {
+        return nullptr;
+    }
+
+    GrBackendFormat backendFormat = GrBackendFormat::MakeVk(vkInfo.fFormat);
+    if (!backendFormat.isValid()) {
+        return nullptr;
+    }
+    GrPixelConfig config = this->caps()->getConfigFromBackendFormat(backendFormat,
+                                                                    imageInfo.colorType());
+    if (config == kUnknown_GrPixelConfig) {
+        return nullptr;
+    }
+
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = imageInfo.width();
+    desc.fHeight = imageInfo.height();
+    desc.fConfig = config;
+    desc.fSampleCnt = this->caps()->getRenderTargetSampleCount(1, config);
+    if (!desc.fSampleCnt) {
+        return nullptr;
+    }
+
+    return GrVkRenderTarget::MakeSecondaryCBRenderTarget(this, desc, vkInfo);
+}
+
 bool GrVkGpu::onRegenerateMipMapLevels(GrTexture* tex) {
     auto* vkTex = static_cast<GrVkTexture*>(tex);
     // don't do anything for linearly tiled textures (can't have mipmaps)
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 3aae807..22cf45e 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -187,6 +187,9 @@
     sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTexture&,
                                                              int sampleCnt) override;
 
+    sk_sp<GrRenderTarget> onWrapVulkanSecondaryCBAsRenderTarget(const SkImageInfo&,
+                                                                const GrVkDrawableInfo&) override;
+
     GrBuffer* onCreateBuffer(size_t size, GrBufferType type, GrAccessPattern,
                              const void* data) override;
 
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index 0729531..ead8d8d 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -209,10 +209,6 @@
     GrVkMemory::FreeImageMemory(gpu, isLinear, info->fAlloc);
 }
 
-void GrVkImage::setNewResource(VkImage image, const GrVkAlloc& alloc, VkImageTiling tiling) {
-    fResource = new Resource(image, alloc, tiling);
-}
-
 GrVkImage::~GrVkImage() {
     // should have been released or abandoned first
     SkASSERT(!fResource);
@@ -238,6 +234,7 @@
 }
 
 void GrVkImage::setResourceRelease(sk_sp<GrReleaseProcHelper> releaseHelper) {
+    SkASSERT(fResource);
     // Forward the release proc on to GrVkImage::Resource
     fResource->setRelease(std::move(releaseHelper));
 }
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 91ae488..0ce223c 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -25,13 +25,15 @@
 
 public:
     GrVkImage(const GrVkImageInfo& info, sk_sp<GrVkImageLayout> layout,
-              GrBackendObjectOwnership ownership)
+              GrBackendObjectOwnership ownership, bool forSecondaryCB = false)
             : fInfo(info)
             , fInitialQueueFamily(info.fCurrentQueueFamily)
             , fLayout(std::move(layout))
             , fIsBorrowed(GrBackendObjectOwnership::kBorrowed == ownership) {
         SkASSERT(fLayout->getImageLayout() == fInfo.fImageLayout);
-        if (fIsBorrowed) {
+        if (forSecondaryCB) {
+            fResource = nullptr;
+        } else if (fIsBorrowed) {
             fResource = new BorrowedResource(info.fImage, info.fAlloc, info.fImageTiling);
         } else {
             fResource = new Resource(info.fImage, info.fAlloc, info.fImageTiling);
@@ -39,18 +41,37 @@
     }
     virtual ~GrVkImage();
 
-    VkImage image() const { return fInfo.fImage; }
-    const GrVkAlloc& alloc() const { return fInfo.fAlloc; }
+    VkImage image() const {
+        // Should only be called when we have a real fResource object, i.e. never when being used as
+        // a RT in an external secondary command buffer.
+        SkASSERT(fResource);
+        return fInfo.fImage;
+    }
+    const GrVkAlloc& alloc() const {
+        // Should only be called when we have a real fResource object, i.e. never when being used as
+        // a RT in an external secondary command buffer.
+        SkASSERT(fResource);
+        return fInfo.fAlloc;
+    }
     VkFormat imageFormat() const { return fInfo.fFormat; }
     GrBackendFormat getBackendFormat() const {
         return GrBackendFormat::MakeVk(this->imageFormat());
     }
     uint32_t mipLevels() const { return fInfo.fLevelCount; }
     const GrVkYcbcrConversionInfo& ycbcrConversionInfo() const {
+        // Should only be called when we have a real fResource object, i.e. never when being used as
+        // a RT in an external secondary command buffer.
+        SkASSERT(fResource);
         return fInfo.fYcbcrConversionInfo;
     }
-    const Resource* resource() const { return fResource; }
+    const Resource* resource() const {
+        SkASSERT(fResource);
+        return fResource;
+    }
     bool isLinearTiled() const {
+        // Should only be called when we have a real fResource object, i.e. never when being used as
+        // a RT in an external secondary command buffer.
+        SkASSERT(fResource);
         return SkToBool(VK_IMAGE_TILING_LINEAR == fInfo.fImageTiling);
     }
     bool isBorrowed() const { return fIsBorrowed; }
@@ -72,6 +93,9 @@
     // This is only used for mip map generation where we are manually changing the layouts as we
     // blit each layer, and then at the end need to update our tracking.
     void updateImageLayout(VkImageLayout newLayout) {
+        // Should only be called when we have a real fResource object, i.e. never when being used as
+        // a RT in an external secondary command buffer.
+        SkASSERT(fResource);
         fLayout->setImageLayout(newLayout);
     }
 
@@ -116,8 +140,6 @@
     void releaseImage(GrVkGpu* gpu);
     void abandonImage();
 
-    void setNewResource(VkImage image, const GrVkAlloc& alloc, VkImageTiling tiling);
-
     GrVkImageInfo          fInfo;
     uint32_t               fInitialQueueFamily;
     sk_sp<GrVkImageLayout> fLayout;
diff --git a/src/gpu/vk/GrVkRenderPass.cpp b/src/gpu/vk/GrVkRenderPass.cpp
index 682244c..7077528 100644
--- a/src/gpu/vk/GrVkRenderPass.cpp
+++ b/src/gpu/vk/GrVkRenderPass.cpp
@@ -164,14 +164,15 @@
 }
 
 void GrVkRenderPass::freeGPUData(GrVkGpu* gpu) const {
-    GR_VK_CALL(gpu->vkInterface(), DestroyRenderPass(gpu->device(), fRenderPass, nullptr));
+    if (!(fAttachmentFlags & kExternal_AttachmentFlag)) {
+        GR_VK_CALL(gpu->vkInterface(), DestroyRenderPass(gpu->device(), fRenderPass, nullptr));
+    }
 }
 
-// Works under the assumption that color attachment will always be the first attachment in our
-// attachment array if it exists.
 bool GrVkRenderPass::colorAttachmentIndex(uint32_t* index) const {
-    *index = 0;
-    if (fAttachmentFlags & kColor_AttachmentFlag) {
+    *index = fColorAttachmentIndex;
+    if ((fAttachmentFlags & kColor_AttachmentFlag) ||
+        (fAttachmentFlags & kExternal_AttachmentFlag)) {
         return true;
     }
     return false;
@@ -192,6 +193,7 @@
 
 bool GrVkRenderPass::isCompatible(const AttachmentsDescriptor& desc,
                                   const AttachmentFlags& flags) const {
+    SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag));
     if (flags != fAttachmentFlags) {
         return false;
     }
@@ -211,6 +213,7 @@
 }
 
 bool GrVkRenderPass::isCompatible(const GrVkRenderTarget& target) const {
+    SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag));
     AttachmentsDescriptor desc;
     AttachmentFlags flags;
     target.getAttachmentsDescriptor(&desc, &flags);
@@ -219,11 +222,18 @@
 }
 
 bool GrVkRenderPass::isCompatible(const GrVkRenderPass& renderPass) const {
+    SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag));
     return this->isCompatible(renderPass.fAttachmentsDescriptor, renderPass.fAttachmentFlags);
 }
 
+bool GrVkRenderPass::isCompatibleExternalRP(VkRenderPass renderPass) const {
+    SkASSERT(fAttachmentFlags & kExternal_AttachmentFlag);
+    return fRenderPass == renderPass;
+}
+
 bool GrVkRenderPass::equalLoadStoreOps(const LoadStoreOps& colorOps,
                                        const LoadStoreOps& stencilOps) const {
+    SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag));
     if (fAttachmentFlags & kColor_AttachmentFlag) {
         if (fAttachmentsDescriptor.fColor.fLoadStoreOps != colorOps) {
             return false;
@@ -247,4 +257,10 @@
         b->add32(fAttachmentsDescriptor.fStencil.fFormat);
         b->add32(fAttachmentsDescriptor.fStencil.fSamples);
     }
+    if (fAttachmentFlags & kExternal_AttachmentFlag) {
+        SkASSERT(!(fAttachmentFlags & ~kExternal_AttachmentFlag));
+        uint64_t handle = (uint64_t)fRenderPass;
+        b->add32((uint32_t)handle);
+        b->add32((uint32_t)(handle>>32));
+    }
 }
diff --git a/src/gpu/vk/GrVkRenderPass.h b/src/gpu/vk/GrVkRenderPass.h
index ce66811..b6e4497 100644
--- a/src/gpu/vk/GrVkRenderPass.h
+++ b/src/gpu/vk/GrVkRenderPass.h
@@ -20,6 +20,15 @@
 public:
     GrVkRenderPass() : INHERITED(), fRenderPass(VK_NULL_HANDLE), fClearValueCount(0) {}
 
+    // Used when importing an external render pass. In this case we have to explicitly be told the
+    // color attachment index
+    explicit GrVkRenderPass(VkRenderPass renderPass, uint32_t colorAttachmentIndex)
+            : INHERITED()
+            , fRenderPass(renderPass)
+            , fAttachmentFlags(kExternal_AttachmentFlag)
+            , fClearValueCount(0)
+            , fColorAttachmentIndex(colorAttachmentIndex) {}
+
     struct LoadStoreOps {
         VkAttachmentLoadOp  fLoadOp;
         VkAttachmentStoreOp fStoreOp;
@@ -78,6 +87,11 @@
     enum AttachmentFlags {
         kColor_AttachmentFlag = 0x1,
         kStencil_AttachmentFlag = 0x2,
+        // The external attachment flag signals that this render pass is imported from an external
+        // client. Since we don't know every attachment on the render pass we don't set any of the
+        // specific attachment flags when using external. However, the external render pass must
+        // at least have a color attachment.
+        kExternal_AttachmentFlag = 0x4,
     };
     GR_DECL_BITFIELD_OPS_FRIENDS(AttachmentFlags);
 
@@ -95,6 +109,8 @@
 
     bool isCompatible(const GrVkRenderPass& renderPass) const;
 
+    bool isCompatibleExternalRP(VkRenderPass) const;
+
     bool equalLoadStoreOps(const LoadStoreOps& colorOps,
                            const LoadStoreOps& stencilOps) const;
 
@@ -131,6 +147,8 @@
     AttachmentsDescriptor fAttachmentsDescriptor;
     VkExtent2D            fGranularity;
     uint32_t              fClearValueCount;
+    // For internally created render passes we assume the color attachment index is always 0.
+    uint32_t              fColorAttachmentIndex = 0;
 
     typedef GrVkResource INHERITED;
 };
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index d1393f9..fcbc470 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -30,15 +30,16 @@
                                    sk_sp<GrVkImageLayout> msaaLayout,
                                    const GrVkImageView* colorAttachmentView,
                                    const GrVkImageView* resolveAttachmentView)
-    : GrSurface(gpu, desc)
-    , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed)
-    // for the moment we only support 1:1 color to stencil
-    , GrRenderTarget(gpu, desc)
-    , fColorAttachmentView(colorAttachmentView)
-    , fMSAAImage(new GrVkImage(msaaInfo, std::move(msaaLayout), GrBackendObjectOwnership::kOwned))
-    , fResolveAttachmentView(resolveAttachmentView)
-    , fFramebuffer(nullptr)
-    , fCachedSimpleRenderPass(nullptr) {
+        : GrSurface(gpu, desc)
+        , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed)
+        // for the moment we only support 1:1 color to stencil
+        , GrRenderTarget(gpu, desc)
+        , fColorAttachmentView(colorAttachmentView)
+        , fMSAAImage(new GrVkImage(msaaInfo, std::move(msaaLayout),
+                                   GrBackendObjectOwnership::kOwned))
+        , fResolveAttachmentView(resolveAttachmentView)
+        , fFramebuffer(nullptr)
+        , fCachedSimpleRenderPass(nullptr) {
     SkASSERT(desc.fSampleCnt > 1);
     this->createFramebuffer(gpu);
     this->registerWithCacheWrapped();
@@ -55,15 +56,16 @@
                                    const GrVkImageView* colorAttachmentView,
                                    const GrVkImageView* resolveAttachmentView,
                                    GrBackendObjectOwnership ownership)
-    : GrSurface(gpu, desc)
-    , GrVkImage(info, std::move(layout), ownership)
-    // for the moment we only support 1:1 color to stencil
-    , GrRenderTarget(gpu, desc)
-    , fColorAttachmentView(colorAttachmentView)
-    , fMSAAImage(new GrVkImage(msaaInfo, std::move(msaaLayout), GrBackendObjectOwnership::kOwned))
-    , fResolveAttachmentView(resolveAttachmentView)
-    , fFramebuffer(nullptr)
-    , fCachedSimpleRenderPass(nullptr) {
+        : GrSurface(gpu, desc)
+        , GrVkImage(info, std::move(layout), ownership)
+        // for the moment we only support 1:1 color to stencil
+        , GrRenderTarget(gpu, desc)
+        , fColorAttachmentView(colorAttachmentView)
+        , fMSAAImage(new GrVkImage(msaaInfo, std::move(msaaLayout),
+                                   GrBackendObjectOwnership::kOwned))
+        , fResolveAttachmentView(resolveAttachmentView)
+        , fFramebuffer(nullptr)
+        , fCachedSimpleRenderPass(nullptr) {
     SkASSERT(desc.fSampleCnt > 1);
     this->createFramebuffer(gpu);
 }
@@ -75,14 +77,14 @@
                                    const GrVkImageInfo& info,
                                    sk_sp<GrVkImageLayout> layout,
                                    const GrVkImageView* colorAttachmentView)
-    : GrSurface(gpu, desc)
-    , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed)
-    , GrRenderTarget(gpu, desc)
-    , fColorAttachmentView(colorAttachmentView)
-    , fMSAAImage(nullptr)
-    , fResolveAttachmentView(nullptr)
-    , fFramebuffer(nullptr)
-    , fCachedSimpleRenderPass(nullptr) {
+        : GrSurface(gpu, desc)
+        , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed)
+        , GrRenderTarget(gpu, desc)
+        , fColorAttachmentView(colorAttachmentView)
+        , fMSAAImage(nullptr)
+        , fResolveAttachmentView(nullptr)
+        , fFramebuffer(nullptr)
+        , fCachedSimpleRenderPass(nullptr) {
     SkASSERT(1 == desc.fSampleCnt);
     this->createFramebuffer(gpu);
     this->registerWithCacheWrapped();
@@ -96,23 +98,39 @@
                                    sk_sp<GrVkImageLayout> layout,
                                    const GrVkImageView* colorAttachmentView,
                                    GrBackendObjectOwnership ownership)
-    : GrSurface(gpu, desc)
-    , GrVkImage(info, std::move(layout), ownership)
-    , GrRenderTarget(gpu, desc)
-    , fColorAttachmentView(colorAttachmentView)
-    , fMSAAImage(nullptr)
-    , fResolveAttachmentView(nullptr)
-    , fFramebuffer(nullptr)
-    , fCachedSimpleRenderPass(nullptr) {
+        : GrSurface(gpu, desc)
+        , GrVkImage(info, std::move(layout), ownership)
+        , GrRenderTarget(gpu, desc)
+        , fColorAttachmentView(colorAttachmentView)
+        , fMSAAImage(nullptr)
+        , fResolveAttachmentView(nullptr)
+        , fFramebuffer(nullptr)
+        , fCachedSimpleRenderPass(nullptr) {
     SkASSERT(1 == desc.fSampleCnt);
     this->createFramebuffer(gpu);
 }
 
-sk_sp<GrVkRenderTarget>
-GrVkRenderTarget::MakeWrappedRenderTarget(GrVkGpu* gpu,
-                                          const GrSurfaceDesc& desc,
-                                          const GrVkImageInfo& info,
-                                          sk_sp<GrVkImageLayout> layout) {
+GrVkRenderTarget::GrVkRenderTarget(GrVkGpu* gpu,
+                                   const GrSurfaceDesc& desc,
+                                   const GrVkImageInfo& info,
+                                   sk_sp<GrVkImageLayout> layout,
+                                   const GrVkRenderPass* renderPass,
+                                   VkCommandBuffer secondaryCommandBuffer)
+        : GrSurface(gpu, desc)
+        , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed, true)
+        , GrRenderTarget(gpu, desc)
+        , fColorAttachmentView(nullptr)
+        , fMSAAImage(nullptr)
+        , fResolveAttachmentView(nullptr)
+        , fFramebuffer(nullptr)
+        , fCachedSimpleRenderPass(renderPass)
+        , fSecondaryCommandBuffer(secondaryCommandBuffer) {
+    this->registerWithCacheWrapped();
+}
+
+sk_sp<GrVkRenderTarget> GrVkRenderTarget::MakeWrappedRenderTarget(
+        GrVkGpu* gpu, const GrSurfaceDesc& desc, const GrVkImageInfo& info,
+        sk_sp<GrVkImageLayout> layout) {
     SkASSERT(VK_NULL_HANDLE != info.fImage);
 
     SkASSERT(1 == info.fLevelCount);
@@ -184,12 +202,37 @@
     return sk_sp<GrVkRenderTarget>(vkRT);
 }
 
+sk_sp<GrVkRenderTarget> GrVkRenderTarget::MakeSecondaryCBRenderTarget(
+        GrVkGpu* gpu, const GrSurfaceDesc& desc, const GrVkDrawableInfo& vkInfo) {
+    // We only set the few properties of the GrVkImageInfo that we know like layout and format. The
+    // others we keep at the default "null" values.
+    GrVkImageInfo info;
+    info.fImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+    info.fFormat = vkInfo.fFormat;
+
+    sk_sp<GrVkImageLayout> layout(new GrVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL));
+
+    const GrVkRenderPass* rp =
+            gpu->resourceProvider().findCompatibleExternalRenderPass(vkInfo.fCompatibleRenderPass,
+                                                                     vkInfo.fColorAttachmentIndex);
+    if (!rp) {
+        return nullptr;
+    }
+
+    GrVkRenderTarget* vkRT = new GrVkRenderTarget(gpu, desc, info, std::move(layout), rp,
+                                                  vkInfo.fSecondaryCommandBuffer);
+
+    return sk_sp<GrVkRenderTarget>(vkRT);
+}
+
 bool GrVkRenderTarget::completeStencilAttachment() {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
     this->createFramebuffer(this->getVkGpu());
     return true;
 }
 
 void GrVkRenderTarget::createFramebuffer(GrVkGpu* gpu) {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
     if (fFramebuffer) {
         fFramebuffer->unref(gpu);
     }
@@ -213,6 +256,7 @@
 void GrVkRenderTarget::getAttachmentsDescriptor(
                                            GrVkRenderPass::AttachmentsDescriptor* desc,
                                            GrVkRenderPass::AttachmentFlags* attachmentFlags) const {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
     VkFormat colorFormat;
     GrPixelConfigToVkFormat(this->config(), &colorFormat);
     desc->fColor.fFormat = colorFormat;
@@ -317,11 +361,13 @@
 
 
 GrBackendRenderTarget GrVkRenderTarget::getBackendRenderTarget() const {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
     return GrBackendRenderTarget(this->width(), this->height(), this->numColorSamples(),
                                  fInfo, this->grVkImageLayout());
 }
 
 const GrVkResource* GrVkRenderTarget::stencilImageResource() const {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
     const GrStencilAttachment* stencil = this->renderTargetPriv().getStencilAttachment();
     if (stencil) {
         const GrVkStencilAttachment* vkStencil = static_cast<const GrVkStencilAttachment*>(stencil);
@@ -332,6 +378,7 @@
 }
 
 const GrVkImageView* GrVkRenderTarget::stencilAttachmentView() const {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
     const GrStencilAttachment* stencil = this->renderTargetPriv().getStencilAttachment();
     if (stencil) {
         const GrVkStencilAttachment* vkStencil = static_cast<const GrVkStencilAttachment*>(stencil);
@@ -341,7 +388,6 @@
     return nullptr;
 }
 
-
 GrVkGpu* GrVkRenderTarget::getVkGpu() const {
     SkASSERT(!this->wasDestroyed());
     return static_cast<GrVkGpu*>(this->getGpu());
diff --git a/src/gpu/vk/GrVkRenderTarget.h b/src/gpu/vk/GrVkRenderTarget.h
index f936a19..dc695fc 100644
--- a/src/gpu/vk/GrVkRenderTarget.h
+++ b/src/gpu/vk/GrVkRenderTarget.h
@@ -36,6 +36,9 @@
                                                            const GrVkImageInfo&,
                                                            sk_sp<GrVkImageLayout>);
 
+    static sk_sp<GrVkRenderTarget> MakeSecondaryCBRenderTarget(GrVkGpu*, const GrSurfaceDesc&,
+                                                               const GrVkDrawableInfo& vkInfo);
+
     ~GrVkRenderTarget() override;
 
     GrBackendFormat backendFormat() const override { return this->getBackendFormat(); }
@@ -55,8 +58,16 @@
 
     const GrVkRenderPass* simpleRenderPass() const { return fCachedSimpleRenderPass; }
     GrVkResourceProvider::CompatibleRPHandle compatibleRenderPassHandle() const {
+        SkASSERT(!this->wrapsSecondaryCommandBuffer());
         return fCompatibleRPHandle;
     }
+    const GrVkRenderPass* externalRenderPass() const {
+        SkASSERT(this->wrapsSecondaryCommandBuffer());
+        // We use the cached simple render pass to hold the external render pass.
+        return fCachedSimpleRenderPass;
+    }
+
+    bool wrapsSecondaryCommandBuffer() const { return fSecondaryCommandBuffer != VK_NULL_HANDLE; }
 
     // override of GrRenderTarget
     ResolveType getResolveType() const override {
@@ -67,7 +78,9 @@
     }
 
     bool canAttemptStencilAttachment() const override {
-        return true;
+        // We don't know the status of the stencil attachment for wrapped external secondary command
+        // buffers so we just assume we don't have one.
+        return !this->wrapsSecondaryCommandBuffer();
     }
 
     GrBackendRenderTarget getBackendRenderTarget() const override;
@@ -133,6 +146,14 @@
                      sk_sp<GrVkImageLayout> layout,
                      const GrVkImageView* colorAttachmentView);
 
+
+    GrVkRenderTarget(GrVkGpu* gpu,
+                     const GrSurfaceDesc& desc,
+                     const GrVkImageInfo& info,
+                     sk_sp<GrVkImageLayout> layout,
+                     const GrVkRenderPass* renderPass,
+                     VkCommandBuffer secondaryCommandBuffer);
+
     bool completeStencilAttachment() override;
 
     void releaseInternalObjects();
@@ -145,6 +166,11 @@
     const GrVkRenderPass*      fCachedSimpleRenderPass;
     // This is a handle to be used to quickly get compatible GrVkRenderPasses for this render target
     GrVkResourceProvider::CompatibleRPHandle fCompatibleRPHandle;
+
+    // Handle to an external secondary command buffer which this GrVkRenderTarget represents. If
+    // this is not VK_NULL_HANDLE then the GrVkRenderTarget does not have a real VkImage backing it,
+    // and is limited in what it can be used for.
+    VkCommandBuffer            fSecondaryCommandBuffer = VK_NULL_HANDLE;
 };
 
 #endif
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 99b4d12..060bc87 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -31,6 +31,7 @@
 
 GrVkResourceProvider::~GrVkResourceProvider() {
     SkASSERT(0 == fRenderPassArray.count());
+    SkASSERT(0 == fExternalRenderPasses.count());
     SkASSERT(VK_NULL_HANDLE == fPipelineCache);
     delete fPipelineStateCache;
 }
@@ -134,6 +135,26 @@
     return renderPass;
 }
 
+const GrVkRenderPass* GrVkResourceProvider::findCompatibleExternalRenderPass(
+        VkRenderPass renderPass, uint32_t colorAttachmentIndex) {
+    for (int i = 0; i < fExternalRenderPasses.count(); ++i) {
+        if (fExternalRenderPasses[i]->isCompatibleExternalRP(renderPass)) {
+            fExternalRenderPasses[i]->ref();
+#ifdef SK_DEBUG
+            uint32_t cachedColorIndex;
+            SkASSERT(fExternalRenderPasses[i]->colorAttachmentIndex(&cachedColorIndex));
+            SkASSERT(cachedColorIndex == colorAttachmentIndex);
+#endif
+            return fExternalRenderPasses[i];
+        }
+    }
+
+    const GrVkRenderPass* newRenderPass = new GrVkRenderPass(renderPass, colorAttachmentIndex);
+    fExternalRenderPasses.push_back(newRenderPass);
+    newRenderPass->ref();
+    return newRenderPass;
+}
+
 const GrVkRenderPass* GrVkResourceProvider::findRenderPass(
                                                      const GrVkRenderTarget& target,
                                                      const GrVkRenderPass::LoadStoreOps& colorOps,
@@ -346,6 +367,11 @@
     }
     fRenderPassArray.reset();
 
+    for (int i = 0; i < fExternalRenderPasses.count(); ++i) {
+        fExternalRenderPasses[i]->unref(fGpu);
+    }
+    fExternalRenderPasses.reset();
+
     // Iterate through all store GrVkSamplers and unref them before resetting the hash.
     SkTDynamicHash<GrVkSampler, GrVkSampler::Key>::Iter iter(&fSamplers);
     for (; !iter.done(); ++iter) {
@@ -414,6 +440,11 @@
     }
     fRenderPassArray.reset();
 
+    for (int i = 0; i < fExternalRenderPasses.count(); ++i) {
+        fExternalRenderPasses[i]->unrefAndAbandon();
+    }
+    fExternalRenderPasses.reset();
+
     // Iterate through all store GrVkSamplers and unrefAndAbandon them before resetting the hash.
     SkTDynamicHash<GrVkSampler, GrVkSampler::Key>::Iter iter(&fSamplers);
     for (; !iter.done(); ++iter) {
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 4b0e00a..2eac2b2 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -69,6 +69,9 @@
     // findCompatibleRenderPass(GrVkRenderTarget&, CompatibleRPHandle*).
     const GrVkRenderPass* findCompatibleRenderPass(const CompatibleRPHandle& compatibleHandle);
 
+    const GrVkRenderPass* findCompatibleExternalRenderPass(VkRenderPass,
+                                                           uint32_t colorAttachmentIndex);
+
     // Finds or creates a render pass that matches the target and LoadStoreOps, increments the
     // refcount, and returns. The caller can optionally pass in a pointer to a CompatibleRPHandle.
     // If this is non null it will be set to a handle that can be used in the furutre to quickly
@@ -252,6 +255,8 @@
 
     SkSTArray<4, CompatibleRenderPassSet> fRenderPassArray;
 
+    SkTArray<const GrVkRenderPass*> fExternalRenderPasses;
+
     // Array of command pools that we are waiting on
     SkSTArray<4, GrVkCommandPool*, true> fActiveCommandPools;
 
diff --git a/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp b/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp
new file mode 100644
index 0000000..a7e5e3f
--- /dev/null
+++ b/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "vk/GrVkSecondaryCBDrawContext.h"
+
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "SkGpuDevice.h"
+#include "SkImageInfo.h"
+#include "SkSurfaceProps.h"
+#include "vk/GrVkTypes.h"
+
+sk_sp<GrVkSecondaryCBDrawContext> GrVkSecondaryCBDrawContext::Make(GrContext* ctx,
+                                                                   const SkImageInfo& imageInfo,
+                                                                   const GrVkDrawableInfo& vkInfo,
+                                                                   const SkSurfaceProps* props) {
+    if (!ctx) {
+        return nullptr;
+    }
+
+    if (ctx->contextPriv().getBackend() != GrBackendApi::kVulkan) {
+        return nullptr;
+    }
+
+    sk_sp<GrRenderTargetContext> rtc(
+            ctx->contextPriv().makeVulkanSecondaryCBRenderTargetContext(imageInfo, vkInfo, props));
+
+    int width = rtc->width();
+    int height = rtc->height();
+
+    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(ctx, std::move(rtc), width, height,
+                                                SkGpuDevice::kUninit_InitContents));
+    if (!device) {
+        return nullptr;
+    }
+
+    return sk_sp<GrVkSecondaryCBDrawContext>(new GrVkSecondaryCBDrawContext(std::move(device)));
+}
+
+GrVkSecondaryCBDrawContext::GrVkSecondaryCBDrawContext(sk_sp<SkGpuDevice> device)
+    : fDevice(device) {}
+
+GrVkSecondaryCBDrawContext::~GrVkSecondaryCBDrawContext() {
+    SkASSERT(!fDevice);
+    SkASSERT(!fCachedCanvas.get());
+}
+
+SkCanvas* GrVkSecondaryCBDrawContext::getCanvas() {
+    if (!fCachedCanvas) {
+        fCachedCanvas = std::unique_ptr<SkCanvas>(new SkCanvas(fDevice));
+    }
+    return fCachedCanvas.get();
+}
+
+void GrVkSecondaryCBDrawContext::flush() {
+    fDevice->flush();
+}
+
+void GrVkSecondaryCBDrawContext::releaseResources() {
+    fCachedCanvas.reset();
+    fDevice.reset();
+}
+