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>
This commit is contained in:
Greg Daniel 2019-01-03 17:35:54 -05:00 committed by Skia Commit-Bot
parent 0ae5f90985
commit 070cbafd6b
8 changed files with 252 additions and 49 deletions

View File

@ -523,9 +523,15 @@ void GrRenderTargetOpList::fullClear(GrContext* context, const SkPMColor4f& colo
if (this->isEmpty() || !fTarget.get()->asRenderTargetProxy()->needsStencil()) { if (this->isEmpty() || !fTarget.get()->asRenderTargetProxy()->needsStencil()) {
this->deleteOps(); this->deleteOps();
fDeferredProxies.reset(); fDeferredProxies.reset();
fColorLoadOp = GrLoadOp::kClear;
fLoadClearColor = color; // If the opList is using a render target which wraps a vulkan command buffer, we can't do a
return; // 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(), std::unique_ptr<GrClearOp> op(GrClearOp::Make(context, GrFixedClip::Disabled(),

View File

@ -58,8 +58,10 @@ void GrVkCommandBuffer::freeGPUData(GrVkGpu* gpu) const {
fTrackedRecordingResources[i]->unref(gpu); fTrackedRecordingResources[i]->unref(gpu);
} }
GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), fCmdPool->vkCommandPool(), 1, if (!this->isWrapped()) {
&fCmdBuffer)); GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), fCmdPool->vkCommandPool(),
1, &fCmdBuffer));
}
this->onFreeGPUData(gpu); this->onFreeGPUData(gpu);
} }
@ -131,6 +133,7 @@ void GrVkCommandBuffer::pipelineBarrier(const GrVkGpu* gpu,
bool byRegion, bool byRegion,
BarrierType barrierType, BarrierType barrierType,
void* barrier) const { void* barrier) const {
SkASSERT(!this->isWrapped());
SkASSERT(fIsActive); SkASSERT(fIsActive);
// For images we can have barriers inside of render passes but they require us to add more // 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 // support in subpasses which need self dependencies to have barriers inside them. Also, we can
@ -835,6 +838,7 @@ void GrVkPrimaryCommandBuffer::onAbandonGPUData() const {
GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(const GrVkGpu* gpu, GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(const GrVkGpu* gpu,
GrVkCommandPool* cmdPool) { GrVkCommandPool* cmdPool) {
SkASSERT(cmdPool);
const VkCommandBufferAllocateInfo cmdInfo = { const VkCommandBufferAllocateInfo cmdInfo = {
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType
nullptr, // pNext nullptr, // pNext
@ -853,6 +857,9 @@ GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(const GrVkGpu* gp
return new GrVkSecondaryCommandBuffer(cmdBuffer, cmdPool); return new GrVkSecondaryCommandBuffer(cmdBuffer, cmdPool);
} }
GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(VkCommandBuffer cmdBuffer) {
return new GrVkSecondaryCommandBuffer(cmdBuffer, nullptr);
}
void GrVkSecondaryCommandBuffer::begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer, void GrVkSecondaryCommandBuffer::begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
const GrVkRenderPass* compatibleRenderPass) { const GrVkRenderPass* compatibleRenderPass) {
@ -860,33 +867,37 @@ void GrVkSecondaryCommandBuffer::begin(const GrVkGpu* gpu, const GrVkFramebuffer
SkASSERT(compatibleRenderPass); SkASSERT(compatibleRenderPass);
fActiveRenderPass = compatibleRenderPass; fActiveRenderPass = compatibleRenderPass;
VkCommandBufferInheritanceInfo inheritanceInfo; if (!this->isWrapped()) {
memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo)); VkCommandBufferInheritanceInfo inheritanceInfo;
inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo));
inheritanceInfo.pNext = nullptr; inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
inheritanceInfo.renderPass = fActiveRenderPass->vkRenderPass(); inheritanceInfo.pNext = nullptr;
inheritanceInfo.subpass = 0; // Currently only using 1 subpass for each render pass inheritanceInfo.renderPass = fActiveRenderPass->vkRenderPass();
inheritanceInfo.framebuffer = framebuffer ? framebuffer->framebuffer() : VK_NULL_HANDLE; inheritanceInfo.subpass = 0; // Currently only using 1 subpass for each render pass
inheritanceInfo.occlusionQueryEnable = false; inheritanceInfo.framebuffer = framebuffer ? framebuffer->framebuffer() : VK_NULL_HANDLE;
inheritanceInfo.queryFlags = 0; inheritanceInfo.occlusionQueryEnable = false;
inheritanceInfo.pipelineStatistics = 0; inheritanceInfo.queryFlags = 0;
inheritanceInfo.pipelineStatistics = 0;
VkCommandBufferBeginInfo cmdBufferBeginInfo; VkCommandBufferBeginInfo cmdBufferBeginInfo;
memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo)); memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufferBeginInfo.pNext = nullptr; cmdBufferBeginInfo.pNext = nullptr;
cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT | cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
cmdBufferBeginInfo.pInheritanceInfo = &inheritanceInfo; cmdBufferBeginInfo.pInheritanceInfo = &inheritanceInfo;
GR_VK_CALL_ERRCHECK(gpu->vkInterface(), BeginCommandBuffer(fCmdBuffer, GR_VK_CALL_ERRCHECK(gpu->vkInterface(), BeginCommandBuffer(fCmdBuffer,
&cmdBufferBeginInfo)); &cmdBufferBeginInfo));
}
fIsActive = true; fIsActive = true;
} }
void GrVkSecondaryCommandBuffer::end(GrVkGpu* gpu) { void GrVkSecondaryCommandBuffer::end(GrVkGpu* gpu) {
SkASSERT(fIsActive); SkASSERT(fIsActive);
GR_VK_CALL_ERRCHECK(gpu->vkInterface(), EndCommandBuffer(fCmdBuffer)); if (!this->isWrapped()) {
GR_VK_CALL_ERRCHECK(gpu->vkInterface(), EndCommandBuffer(fCmdBuffer));
}
this->invalidateState(); this->invalidateState();
fIsActive = false; fIsActive = false;
} }

View File

@ -132,7 +132,7 @@ public:
protected: protected:
GrVkCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool, GrVkCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool,
const GrVkRenderPass* rp = VK_NULL_HANDLE) const GrVkRenderPass* rp = nullptr)
: fIsActive(false) : fIsActive(false)
, fActiveRenderPass(rp) , fActiveRenderPass(rp)
, fCmdBuffer(cmdBuffer) , fCmdBuffer(cmdBuffer)
@ -144,6 +144,10 @@ protected:
this->invalidateState(); this->invalidateState();
} }
bool isWrapped() const {
return fCmdPool == nullptr;
}
SkTDArray<const GrVkResource*> fTrackedResources; SkTDArray<const GrVkResource*> fTrackedResources;
SkTDArray<const GrVkRecycledResource*> fTrackedRecycledResources; SkTDArray<const GrVkRecycledResource*> fTrackedRecycledResources;
SkTDArray<const GrVkResource*> fTrackedRecordingResources; SkTDArray<const GrVkResource*> fTrackedRecordingResources;
@ -326,6 +330,8 @@ private:
class GrVkSecondaryCommandBuffer : public GrVkCommandBuffer { class GrVkSecondaryCommandBuffer : public GrVkCommandBuffer {
public: public:
static GrVkSecondaryCommandBuffer* Create(const GrVkGpu* gpu, GrVkCommandPool* cmdPool); 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, void begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
const GrVkRenderPass* compatibleRenderPass); const GrVkRenderPass* compatibleRenderPass);

View File

@ -127,6 +127,21 @@ void GrVkGpuRTCommandBuffer::init() {
cbInfo.currentCmdBuf()->begin(fGpu, vkRT->framebuffer(), cbInfo.fRenderPass); 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() { GrVkGpuRTCommandBuffer::~GrVkGpuRTCommandBuffer() {
this->reset(); this->reset();
@ -176,6 +191,23 @@ void GrVkGpuRTCommandBuffer::submit() {
continue; 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. // 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 // TODO: Once we improve our tracking of discards so that we never end up flushing a discard
// call with no actually ops, remove this. // call with no actually ops, remove this.
@ -244,6 +276,11 @@ void GrVkGpuRTCommandBuffer::set(GrRenderTarget* rt, GrSurfaceOrigin origin,
this->INHERITED::set(rt, origin); this->INHERITED::set(rt, origin);
if (this->wrapsSecondaryCommandBuffer()) {
this->initWrapped();
return;
}
fClearColor = colorInfo.fClearColor; fClearColor = colorInfo.fClearColor;
get_vk_load_store_ops(colorInfo.fLoadOp, colorInfo.fStoreOp, get_vk_load_store_ops(colorInfo.fLoadOp, colorInfo.fStoreOp,
@ -271,6 +308,11 @@ void GrVkGpuRTCommandBuffer::reset() {
fRenderTarget = nullptr; fRenderTarget = nullptr;
} }
bool GrVkGpuRTCommandBuffer::wrapsSecondaryCommandBuffer() const {
GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget);
return vkRT->wrapsSecondaryCommandBuffer();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void GrVkGpuRTCommandBuffer::discard() { void GrVkGpuRTCommandBuffer::discard() {

View File

@ -87,6 +87,12 @@ public:
private: private:
void init(); 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; GrGpu* gpu() override;
// Bind vertex and index buffers // Bind vertex and index buffers

View File

@ -115,7 +115,7 @@ GrVkRenderTarget::GrVkRenderTarget(GrVkGpu* gpu,
const GrVkImageInfo& info, const GrVkImageInfo& info,
sk_sp<GrVkImageLayout> layout, sk_sp<GrVkImageLayout> layout,
const GrVkRenderPass* renderPass, const GrVkRenderPass* renderPass,
VkCommandBuffer secondaryCommandBuffer) GrVkSecondaryCommandBuffer* secondaryCommandBuffer)
: GrSurface(gpu, desc) : GrSurface(gpu, desc)
, GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed, true) , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kBorrowed, true)
, GrRenderTarget(gpu, desc) , GrRenderTarget(gpu, desc)
@ -219,8 +219,13 @@ sk_sp<GrVkRenderTarget> GrVkRenderTarget::MakeSecondaryCBRenderTarget(
return nullptr; return nullptr;
} }
GrVkRenderTarget* vkRT = new GrVkRenderTarget(gpu, desc, info, std::move(layout), rp, GrVkSecondaryCommandBuffer* scb =
vkInfo.fSecondaryCommandBuffer); 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); return sk_sp<GrVkRenderTarget>(vkRT);
} }
@ -284,6 +289,7 @@ GrVkRenderTarget::~GrVkRenderTarget() {
SkASSERT(!fColorAttachmentView); SkASSERT(!fColorAttachmentView);
SkASSERT(!fFramebuffer); SkASSERT(!fFramebuffer);
SkASSERT(!fCachedSimpleRenderPass); SkASSERT(!fCachedSimpleRenderPass);
SkASSERT(!fSecondaryCommandBuffer);
} }
void GrVkRenderTarget::addResources(GrVkCommandBuffer& commandBuffer) const { void GrVkRenderTarget::addResources(GrVkCommandBuffer& commandBuffer) const {
@ -321,6 +327,10 @@ void GrVkRenderTarget::releaseInternalObjects() {
fCachedSimpleRenderPass->unref(gpu); fCachedSimpleRenderPass->unref(gpu);
fCachedSimpleRenderPass = nullptr; fCachedSimpleRenderPass = nullptr;
} }
if (fSecondaryCommandBuffer) {
fSecondaryCommandBuffer->unref(gpu);
fSecondaryCommandBuffer = nullptr;
}
} }
void GrVkRenderTarget::abandonInternalObjects() { void GrVkRenderTarget::abandonInternalObjects() {
@ -345,6 +355,10 @@ void GrVkRenderTarget::abandonInternalObjects() {
fCachedSimpleRenderPass->unrefAndAbandon(); fCachedSimpleRenderPass->unrefAndAbandon();
fCachedSimpleRenderPass = nullptr; fCachedSimpleRenderPass = nullptr;
} }
if (fSecondaryCommandBuffer) {
fSecondaryCommandBuffer->unrefAndAbandon();
fSecondaryCommandBuffer = nullptr;
}
} }
void GrVkRenderTarget::onRelease() { void GrVkRenderTarget::onRelease() {

View File

@ -20,6 +20,7 @@ class GrVkCommandBuffer;
class GrVkFramebuffer; class GrVkFramebuffer;
class GrVkGpu; class GrVkGpu;
class GrVkImageView; class GrVkImageView;
class GrVkSecondaryCommandBuffer;
class GrVkStencilAttachment; class GrVkStencilAttachment;
struct GrVkImageInfo; struct GrVkImageInfo;
@ -67,7 +68,10 @@ public:
return fCachedSimpleRenderPass; return fCachedSimpleRenderPass;
} }
bool wrapsSecondaryCommandBuffer() const { return fSecondaryCommandBuffer != VK_NULL_HANDLE; } bool wrapsSecondaryCommandBuffer() const { return fSecondaryCommandBuffer != nullptr; }
GrVkSecondaryCommandBuffer* getExternalSecondaryCommandBuffer() const {
return fSecondaryCommandBuffer;
}
// override of GrRenderTarget // override of GrRenderTarget
ResolveType getResolveType() const override { ResolveType getResolveType() const override {
@ -152,7 +156,7 @@ private:
const GrVkImageInfo& info, const GrVkImageInfo& info,
sk_sp<GrVkImageLayout> layout, sk_sp<GrVkImageLayout> layout,
const GrVkRenderPass* renderPass, const GrVkRenderPass* renderPass,
VkCommandBuffer secondaryCommandBuffer); GrVkSecondaryCommandBuffer* secondaryCommandBuffer);
bool completeStencilAttachment() override; bool completeStencilAttachment() override;
@ -167,10 +171,11 @@ private:
// This is a handle to be used to quickly get compatible GrVkRenderPasses for this render target // This is a handle to be used to quickly get compatible GrVkRenderPasses for this render target
GrVkResourceProvider::CompatibleRPHandle fCompatibleRPHandle; GrVkResourceProvider::CompatibleRPHandle fCompatibleRPHandle;
// Handle to an external secondary command buffer which this GrVkRenderTarget represents. If // If this render target wraps an external VkCommandBuffer, then this pointer will be non-null
// this is not VK_NULL_HANDLE then the GrVkRenderTarget does not have a real VkImage backing it, // and will point to the GrVk object that, in turn, wraps the external VkCommandBuffer. In this
// and is limited in what it can be used for. // case the render target will not be backed by an actual VkImage and will thus be limited in
VkCommandBuffer fSecondaryCommandBuffer = VK_NULL_HANDLE; // terms of what it can be used for.
GrVkSecondaryCommandBuffer* fSecondaryCommandBuffer = nullptr;
}; };
#endif #endif

View File

@ -22,6 +22,7 @@
#include "vk/GrVkGpu.h" #include "vk/GrVkGpu.h"
#include "vk/GrVkInterface.h" #include "vk/GrVkInterface.h"
#include "vk/GrVkMemory.h" #include "vk/GrVkMemory.h"
#include "vk/GrVkSecondaryCBDrawContext.h"
#include "vk/GrVkUtil.h" #include "vk/GrVkUtil.h"
using sk_gpu_test::GrContextFactory; using sk_gpu_test::GrContextFactory;
@ -30,22 +31,23 @@ static const int DEV_W = 16, DEV_H = 16;
class TestDrawable : public SkDrawable { class TestDrawable : public SkDrawable {
public: public:
TestDrawable(const GrVkInterface* interface, int32_t width, int32_t height) TestDrawable(const GrVkInterface* interface, GrContext* context, int32_t width, int32_t height)
: INHERITED() : INHERITED()
, fInterface(interface) , fInterface(interface)
, fContext(context)
, fWidth(width) , fWidth(width)
, fHeight(height) {} , fHeight(height) {}
~TestDrawable() override {} ~TestDrawable() override {}
class DrawHandler : public GpuDrawHandler { class DrawHandlerBasic : public GpuDrawHandler {
public: public:
DrawHandler(const GrVkInterface* interface, int32_t width, int32_t height) DrawHandlerBasic(const GrVkInterface* interface, int32_t width, int32_t height)
: INHERITED() : INHERITED()
, fInterface(interface) , fInterface(interface)
, fWidth(width) , fWidth(width)
, fHeight(height) {} , fHeight(height) {}
~DrawHandler() override {} ~DrawHandlerBasic() override {}
void draw(const GrBackendDrawableInfo& info) override { void draw(const GrBackendDrawableInfo& info) override {
GrVkDrawableInfo vkInfo; GrVkDrawableInfo vkInfo;
@ -86,14 +88,100 @@ public:
typedef GpuDrawHandler INHERITED; typedef GpuDrawHandler INHERITED;
}; };
typedef void (*DrawProc)(TestDrawable*, const GrVkDrawableInfo&);
typedef void (*SubmitProc)(TestDrawable*);
// Exercises the exporting of a secondary command buffer from one GrContext and then importing
// it into a second GrContext. We then draw to the secondary command buffer from the second
// GrContext.
class DrawHandlerImport : public GpuDrawHandler {
public:
DrawHandlerImport(TestDrawable* td, DrawProc drawProc, SubmitProc submitProc)
: INHERITED()
, fTestDrawable(td)
, fDrawProc(drawProc)
, fSubmitProc(submitProc) {}
~DrawHandlerImport() override {
fSubmitProc(fTestDrawable);
}
void draw(const GrBackendDrawableInfo& info) override {
GrVkDrawableInfo vkInfo;
SkAssertResult(info.getVkDrawableInfo(&vkInfo));
fDrawProc(fTestDrawable, vkInfo);
}
private:
TestDrawable* fTestDrawable;
DrawProc fDrawProc;
SubmitProc fSubmitProc;
typedef GpuDrawHandler INHERITED;
};
// Helper function to test drawing to a secondary command buffer that we imported into the
// GrContext using a GrVkSecondaryCBDrawContext.
static void ImportDraw(TestDrawable* td, const GrVkDrawableInfo& info) {
SkImageInfo imageInfo = SkImageInfo::Make(td->fWidth, td->fHeight, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
td->fDrawContext = GrVkSecondaryCBDrawContext::Make(td->fContext, imageInfo, info, nullptr);
if (!td->fDrawContext) {
return;
}
SkCanvas* canvas = td->fDrawContext->getCanvas();
SkIRect rect = SkIRect::MakeXYWH(td->fWidth/2, 0, td->fWidth/4, td->fHeight);
SkPaint paint;
paint.setColor(SK_ColorRED);
canvas->drawIRect(rect, paint);
// Draw to an offscreen target so that we end up with a mix of "real" secondary command
// buffers and the imported secondary command buffer.
sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(td->fContext, SkBudgeted::kYes,
imageInfo);
surf->getCanvas()->clear(SK_ColorRED);
SkRect dstRect = SkRect::MakeXYWH(3*td->fWidth/4, 0, td->fWidth/4, td->fHeight);
SkIRect srcRect = SkIRect::MakeWH(td->fWidth/4, td->fHeight);
canvas->drawImageRect(surf->makeImageSnapshot(), srcRect, dstRect, &paint);
td->fDrawContext->flush();
}
// Helper function to test waiting for the imported secondary command buffer to be submitted on
// its original context and then cleaning up the GrVkSecondaryCBDrawContext from this GrContext.
static void ImportSubmitted(TestDrawable* td) {
// Typical use case here would be to create a fence that we submit to the gpu and then wait
// on before releasing the GrVkSecondaryCBDrawContext resources. To simulate that for this
// test (and since we are running single threaded anyways), we will just force a sync of
// the gpu and cpu here.
td->fContext->contextPriv().getGpu()->testingOnly_flushGpuAndSync();
td->fDrawContext->releaseResources();
// We release the GrContext here manually to test that we waited long enough before
// releasing the GrVkSecondaryCBDrawContext. This simulates when a client is able to delete
// the GrContext it used to imported the secondary command buffer. If we had released the
// GrContext's resources earlier (before waiting on the gpu above), we would get vulkan
// validation layer errors saying we freed some vulkan objects while they were still in use
// on the GPU.
td->fContext->releaseResourcesAndAbandonContext();
}
std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi, std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
const SkMatrix& matrix, const SkMatrix& matrix,
const SkIRect& clipBounds) override { const SkIRect& clipBounds) override {
if (backendApi != GrBackendApi::kVulkan) { if (backendApi != GrBackendApi::kVulkan) {
return nullptr; return nullptr;
} }
std::unique_ptr<DrawHandler> draw(new DrawHandler(fInterface, fWidth, fHeight)); std::unique_ptr<GpuDrawHandler> draw;
return std::move(draw); if (fContext) {
draw.reset(new DrawHandlerImport(this, ImportDraw, ImportSubmitted));
} else {
draw.reset(new DrawHandlerBasic(fInterface, fWidth, fHeight));
}
return draw;
} }
SkRect onGetBounds() override { SkRect onGetBounds() override {
@ -106,13 +194,15 @@ public:
private: private:
const GrVkInterface* fInterface; const GrVkInterface* fInterface;
int32_t fWidth; GrContext* fContext;
int32_t fHeight; sk_sp<GrVkSecondaryCBDrawContext> fDrawContext;
int32_t fWidth;
int32_t fHeight;
typedef SkDrawable INHERITED; typedef SkDrawable INHERITED;
}; };
void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context) { void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context, GrContext* childContext) {
GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu()); GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu());
const SkImageInfo ii = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType, const SkImageInfo ii = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType,
@ -122,7 +212,7 @@ void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context) {
SkCanvas* canvas = surface->getCanvas(); SkCanvas* canvas = surface->getCanvas();
canvas->clear(SK_ColorBLUE); canvas->clear(SK_ColorBLUE);
sk_sp<TestDrawable> drawable(new TestDrawable(gpu->vkInterface(), DEV_W, DEV_H)); sk_sp<TestDrawable> drawable(new TestDrawable(gpu->vkInterface(), childContext, DEV_W, DEV_H));
canvas->drawDrawable(drawable.get()); canvas->drawDrawable(drawable.get());
SkPaint paint; SkPaint paint;
@ -138,8 +228,8 @@ void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context) {
const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels()); const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
bool failureFound = false; bool failureFound = false;
SkPMColor expectedPixel; SkPMColor expectedPixel;
for (int cy = 0; cy < DEV_H || failureFound; ++cy) { for (int cy = 0; cy < DEV_H && !failureFound; ++cy) {
for (int cx = 0; cx < DEV_W || failureFound; ++cx) { for (int cx = 0; cx < DEV_W && !failureFound; ++cx) {
SkPMColor canvasPixel = canvasPixels[cy * DEV_W + cx]; SkPMColor canvasPixel = canvasPixels[cy * DEV_W + cx];
if (cy < DEV_H / 2) { if (cy < DEV_H / 2) {
if (cx < DEV_W / 2) { if (cx < DEV_W / 2) {
@ -159,9 +249,32 @@ void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context) {
} }
} }
DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkDrawableTest, reporter, ctxInfo) { DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkDrawableTest, reporter, ctxInfo) {
draw_drawable_test(reporter, ctxInfo.grContext()); draw_drawable_test(reporter, ctxInfo.grContext(), nullptr);
}
DEF_GPUTEST(VkDrawableImportTest, reporter, options) {
for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) {
sk_gpu_test::GrContextFactory::ContextType contextType =
(sk_gpu_test::GrContextFactory::ContextType) typeInt;
if (contextType != sk_gpu_test::GrContextFactory::kVulkan_ContextType) {
continue;
}
sk_gpu_test::GrContextFactory factory(options);
sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(
contextType, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR);
skiatest::ReporterContext ctx(
reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
if (ctxInfo.grContext()) {
sk_gpu_test::ContextInfo child =
factory.getSharedContextInfo(ctxInfo.grContext(), 0);
if (!child.grContext()) {
continue;
}
draw_drawable_test(reporter, ctxInfo.grContext(), child.grContext());
}
}
} }
#endif #endif