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