Add support to gpu command buffers to wrap an external command buffer.

Bug: skia:
Change-Id: Ic679d292f42c61f9f1c36315ae605504a0283306
Reviewed-on: https://skia-review.googlesource.com/c/179521
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index b42b265..b77f906 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -523,9 +523,15 @@
     if (this->isEmpty() || !fTarget.get()->asRenderTargetProxy()->needsStencil()) {
         this->deleteOps();
         fDeferredProxies.reset();
-        fColorLoadOp = GrLoadOp::kClear;
-        fLoadClearColor = color;
-        return;
+
+        // If the opList is using a render target which wraps a vulkan command buffer, we can't do a
+        // clear load since we cannot change the render pass that we are using. Thus we fall back to
+        // making a clear op in this case.
+        if (!fTarget.get()->asRenderTargetProxy()->wrapsVkSecondaryCB()) {
+            fColorLoadOp = GrLoadOp::kClear;
+            fLoadClearColor = color;
+            return;
+        }
     }
 
     std::unique_ptr<GrClearOp> op(GrClearOp::Make(context, GrFixedClip::Disabled(),
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 1d75fff..09e8aba 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -58,8 +58,10 @@
         fTrackedRecordingResources[i]->unref(gpu);
     }
 
-    GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), fCmdPool->vkCommandPool(), 1,
-                                                      &fCmdBuffer));
+    if (!this->isWrapped()) {
+        GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), fCmdPool->vkCommandPool(),
+                                                          1, &fCmdBuffer));
+    }
 
     this->onFreeGPUData(gpu);
 }
@@ -131,6 +133,7 @@
                                         bool byRegion,
                                         BarrierType barrierType,
                                         void* barrier) const {
+    SkASSERT(!this->isWrapped());
     SkASSERT(fIsActive);
     // For images we can have barriers inside of render passes but they require us to add more
     // support in subpasses which need self dependencies to have barriers inside them. Also, we can
@@ -835,6 +838,7 @@
 
 GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(const GrVkGpu* gpu,
                                                                GrVkCommandPool* cmdPool) {
+    SkASSERT(cmdPool);
     const VkCommandBufferAllocateInfo cmdInfo = {
         VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
         nullptr,                                          // pNext
@@ -853,6 +857,9 @@
     return new GrVkSecondaryCommandBuffer(cmdBuffer, cmdPool);
 }
 
+GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(VkCommandBuffer cmdBuffer) {
+    return new GrVkSecondaryCommandBuffer(cmdBuffer, nullptr);
+}
 
 void GrVkSecondaryCommandBuffer::begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
                                        const GrVkRenderPass* compatibleRenderPass) {
@@ -860,33 +867,37 @@
     SkASSERT(compatibleRenderPass);
     fActiveRenderPass = compatibleRenderPass;
 
-    VkCommandBufferInheritanceInfo inheritanceInfo;
-    memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo));
-    inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
-    inheritanceInfo.pNext = nullptr;
-    inheritanceInfo.renderPass = fActiveRenderPass->vkRenderPass();
-    inheritanceInfo.subpass = 0; // Currently only using 1 subpass for each render pass
-    inheritanceInfo.framebuffer = framebuffer ? framebuffer->framebuffer() : VK_NULL_HANDLE;
-    inheritanceInfo.occlusionQueryEnable = false;
-    inheritanceInfo.queryFlags = 0;
-    inheritanceInfo.pipelineStatistics = 0;
+    if (!this->isWrapped()) {
+        VkCommandBufferInheritanceInfo inheritanceInfo;
+        memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo));
+        inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
+        inheritanceInfo.pNext = nullptr;
+        inheritanceInfo.renderPass = fActiveRenderPass->vkRenderPass();
+        inheritanceInfo.subpass = 0; // Currently only using 1 subpass for each render pass
+        inheritanceInfo.framebuffer = framebuffer ? framebuffer->framebuffer() : VK_NULL_HANDLE;
+        inheritanceInfo.occlusionQueryEnable = false;
+        inheritanceInfo.queryFlags = 0;
+        inheritanceInfo.pipelineStatistics = 0;
 
-    VkCommandBufferBeginInfo cmdBufferBeginInfo;
-    memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
-    cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-    cmdBufferBeginInfo.pNext = nullptr;
-    cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
-                               VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
-    cmdBufferBeginInfo.pInheritanceInfo = &inheritanceInfo;
+        VkCommandBufferBeginInfo cmdBufferBeginInfo;
+        memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
+        cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+        cmdBufferBeginInfo.pNext = nullptr;
+        cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
+                VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+        cmdBufferBeginInfo.pInheritanceInfo = &inheritanceInfo;
 
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), BeginCommandBuffer(fCmdBuffer,
-                                                               &cmdBufferBeginInfo));
+        GR_VK_CALL_ERRCHECK(gpu->vkInterface(), BeginCommandBuffer(fCmdBuffer,
+                                                                   &cmdBufferBeginInfo));
+    }
     fIsActive = true;
 }
 
 void GrVkSecondaryCommandBuffer::end(GrVkGpu* gpu) {
     SkASSERT(fIsActive);
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), EndCommandBuffer(fCmdBuffer));
+    if (!this->isWrapped()) {
+        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 d30b767..ab11565 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -132,7 +132,7 @@
 
 protected:
         GrVkCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool,
-                          const GrVkRenderPass* rp = VK_NULL_HANDLE)
+                          const GrVkRenderPass* rp = nullptr)
             : fIsActive(false)
             , fActiveRenderPass(rp)
             , fCmdBuffer(cmdBuffer)
@@ -144,6 +144,10 @@
             this->invalidateState();
         }
 
+        bool isWrapped() const {
+            return fCmdPool == nullptr;
+        }
+
         SkTDArray<const GrVkResource*>          fTrackedResources;
         SkTDArray<const GrVkRecycledResource*>  fTrackedRecycledResources;
         SkTDArray<const GrVkResource*>          fTrackedRecordingResources;
@@ -326,6 +330,8 @@
 class GrVkSecondaryCommandBuffer : public GrVkCommandBuffer {
 public:
     static GrVkSecondaryCommandBuffer* Create(const GrVkGpu* gpu, GrVkCommandPool* cmdPool);
+    // Used for wrapping an external secondary command buffer.
+    static GrVkSecondaryCommandBuffer* Create(VkCommandBuffer externalSecondaryCB);
 
     void begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
                const GrVkRenderPass* compatibleRenderPass);
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index faeba92..ef6ec6d 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -127,6 +127,21 @@
     cbInfo.currentCmdBuf()->begin(fGpu, vkRT->framebuffer(), cbInfo.fRenderPass);
 }
 
+void GrVkGpuRTCommandBuffer::initWrapped() {
+    CommandBufferInfo& cbInfo = fCommandBufferInfos.push_back();
+    SkASSERT(fCommandBufferInfos.count() == 1);
+    fCurrentCmdInfo = 0;
+
+    GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget);
+    SkASSERT(vkRT->wrapsSecondaryCommandBuffer());
+    cbInfo.fRenderPass = vkRT->externalRenderPass();
+    cbInfo.fRenderPass->ref();
+
+    cbInfo.fBounds.setEmpty();
+    cbInfo.fCommandBuffers.push_back(vkRT->getExternalSecondaryCommandBuffer());
+    cbInfo.fCommandBuffers[0]->ref();
+    cbInfo.currentCmdBuf()->begin(fGpu, nullptr, cbInfo.fRenderPass);
+}
 
 GrVkGpuRTCommandBuffer::~GrVkGpuRTCommandBuffer() {
     this->reset();
@@ -176,6 +191,23 @@
             continue;
         }
 
+        // We don't want to actually submit the secondary command buffer if it is wrapped.
+        if (this->wrapsSecondaryCommandBuffer()) {
+            // If we have any sampled images set their layout now.
+            for (int j = 0; j < cbInfo.fSampledImages.count(); ++j) {
+                cbInfo.fSampledImages[j]->setImageLayout(fGpu,
+                                                         VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+                                                         VK_ACCESS_SHADER_READ_BIT,
+                                                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+                                                         false);
+            }
+
+            // There should have only been one secondary command buffer in the wrapped case so it is
+            // safe to just return here.
+            SkASSERT(fCommandBufferInfos.count() == 1);
+            return;
+        }
+
         // Make sure if we only have a discard load that we execute the discard on the whole image.
         // TODO: Once we improve our tracking of discards so that we never end up flushing a discard
         // call with no actually ops, remove this.
@@ -244,6 +276,11 @@
 
     this->INHERITED::set(rt, origin);
 
+    if (this->wrapsSecondaryCommandBuffer()) {
+        this->initWrapped();
+        return;
+    }
+
     fClearColor = colorInfo.fClearColor;
 
     get_vk_load_store_ops(colorInfo.fLoadOp, colorInfo.fStoreOp,
@@ -271,6 +308,11 @@
     fRenderTarget = nullptr;
 }
 
+bool GrVkGpuRTCommandBuffer::wrapsSecondaryCommandBuffer() const {
+    GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget);
+    return vkRT->wrapsSecondaryCommandBuffer();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrVkGpuRTCommandBuffer::discard() {
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index a4ed1fa..9dac586 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -87,6 +87,12 @@
 private:
     void init();
 
+    // Called instead of init when we are drawing to a render target that already wraps a secondary
+    // command buffer.
+    void initWrapped();
+
+    bool wrapsSecondaryCommandBuffer() const;
+
     GrGpu* gpu() override;
 
     // Bind vertex and index buffers
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index fcbc470..36dcb60 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -115,7 +115,7 @@
                                    const GrVkImageInfo& info,
                                    sk_sp<GrVkImageLayout> layout,
                                    const GrVkRenderPass* renderPass,
-                                   VkCommandBuffer secondaryCommandBuffer)
+                                   GrVkSecondaryCommandBuffer* secondaryCommandBuffer)
         : GrSurface(gpu, desc)
         , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed, true)
         , GrRenderTarget(gpu, desc)
@@ -219,8 +219,13 @@
         return nullptr;
     }
 
-    GrVkRenderTarget* vkRT = new GrVkRenderTarget(gpu, desc, info, std::move(layout), rp,
-                                                  vkInfo.fSecondaryCommandBuffer);
+    GrVkSecondaryCommandBuffer* scb =
+            GrVkSecondaryCommandBuffer::Create(vkInfo.fSecondaryCommandBuffer);
+    if (!scb) {
+        return nullptr;
+    }
+
+    GrVkRenderTarget* vkRT = new GrVkRenderTarget(gpu, desc, info, std::move(layout), rp, scb);
 
     return sk_sp<GrVkRenderTarget>(vkRT);
 }
@@ -284,6 +289,7 @@
     SkASSERT(!fColorAttachmentView);
     SkASSERT(!fFramebuffer);
     SkASSERT(!fCachedSimpleRenderPass);
+    SkASSERT(!fSecondaryCommandBuffer);
 }
 
 void GrVkRenderTarget::addResources(GrVkCommandBuffer& commandBuffer) const {
@@ -321,6 +327,10 @@
         fCachedSimpleRenderPass->unref(gpu);
         fCachedSimpleRenderPass = nullptr;
     }
+    if (fSecondaryCommandBuffer) {
+        fSecondaryCommandBuffer->unref(gpu);
+        fSecondaryCommandBuffer = nullptr;
+    }
 }
 
 void GrVkRenderTarget::abandonInternalObjects() {
@@ -345,6 +355,10 @@
         fCachedSimpleRenderPass->unrefAndAbandon();
         fCachedSimpleRenderPass = nullptr;
     }
+    if (fSecondaryCommandBuffer) {
+        fSecondaryCommandBuffer->unrefAndAbandon();
+        fSecondaryCommandBuffer = nullptr;
+    }
 }
 
 void GrVkRenderTarget::onRelease() {
diff --git a/src/gpu/vk/GrVkRenderTarget.h b/src/gpu/vk/GrVkRenderTarget.h
index dc695fc..6ee547d 100644
--- a/src/gpu/vk/GrVkRenderTarget.h
+++ b/src/gpu/vk/GrVkRenderTarget.h
@@ -20,6 +20,7 @@
 class GrVkFramebuffer;
 class GrVkGpu;
 class GrVkImageView;
+class GrVkSecondaryCommandBuffer;
 class GrVkStencilAttachment;
 
 struct GrVkImageInfo;
@@ -67,7 +68,10 @@
         return fCachedSimpleRenderPass;
     }
 
-    bool wrapsSecondaryCommandBuffer() const { return fSecondaryCommandBuffer != VK_NULL_HANDLE; }
+    bool wrapsSecondaryCommandBuffer() const { return fSecondaryCommandBuffer != nullptr; }
+    GrVkSecondaryCommandBuffer* getExternalSecondaryCommandBuffer() const {
+        return fSecondaryCommandBuffer;
+    }
 
     // override of GrRenderTarget
     ResolveType getResolveType() const override {
@@ -152,7 +156,7 @@
                      const GrVkImageInfo& info,
                      sk_sp<GrVkImageLayout> layout,
                      const GrVkRenderPass* renderPass,
-                     VkCommandBuffer secondaryCommandBuffer);
+                     GrVkSecondaryCommandBuffer* secondaryCommandBuffer);
 
     bool completeStencilAttachment() override;
 
@@ -167,10 +171,11 @@
     // 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;
+    // If this render target wraps an external VkCommandBuffer, then this pointer will be non-null
+    // and will point to the GrVk object that, in turn, wraps the external VkCommandBuffer. In this
+    // case the render target will not be backed by an actual VkImage and will thus be limited in
+    // terms of what it can be used for.
+    GrVkSecondaryCommandBuffer* fSecondaryCommandBuffer = nullptr;
 };
 
 #endif