Add drawDrawable support to GrRenderTargetContext.

This also includes adding drawable Op and plumbing it through to the GPU.

BUG=skia:

Change-Id: I0b2464c5a458c2fbf05b9528e47b9e6e3ac27d57
Reviewed-on: https://skia-review.googlesource.com/c/9645
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index c18acfe..3c33b26 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -249,10 +249,13 @@
   "$_src/gpu/ops/GrDashLinePathRenderer.h",
   "$_src/gpu/ops/GrDashOp.cpp",
   "$_src/gpu/ops/GrDashOp.h",
+  "$_src/gpu/ops/GrDebugMarkerOp.h",
   "$_src/gpu/ops/GrDefaultPathRenderer.cpp",
   "$_src/gpu/ops/GrDefaultPathRenderer.h",
   "$_src/gpu/ops/GrDebugMarkerOp.cpp",
   "$_src/gpu/ops/GrDebugMarkerOp.h",
+  "$_src/gpu/ops/GrDrawableOp.cpp",
+  "$_src/gpu/ops/GrDrawableOp.h",
   "$_src/gpu/ops/GrDrawAtlasOp.cpp",
   "$_src/gpu/ops/GrDrawAtlasOp.h",
   "$_src/gpu/ops/GrDrawOp.h",
diff --git a/gn/tests.gni b/gn/tests.gni
index 36dd36c..efd3418 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -283,6 +283,7 @@
   "$_tests/UtilsTest.cpp",
   "$_tests/VerticesTest.cpp",
   "$_tests/VkBackendSurfaceTest.cpp",
+  "$_tests/VkDrawableTest.cpp",
   "$_tests/VkMakeCopyPipelineTest.cpp",
   "$_tests/VkWrapTests.cpp",
   "$_tests/VptrTest.cpp",
diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h
index c1b44b4..888d459 100644
--- a/src/gpu/GrGpuCommandBuffer.h
+++ b/src/gpu/GrGpuCommandBuffer.h
@@ -10,6 +10,7 @@
 
 #include "GrColor.h"
 #include "GrPipeline.h"
+#include "SkDrawable.h"
 #include "ops/GrDrawOp.h"
 
 class GrOpFlushState;
@@ -19,6 +20,7 @@
 class GrPipeline;
 class GrPrimitiveProcessor;
 class GrRenderTarget;
+class GrSemaphore;
 struct SkIRect;
 struct SkRect;
 
@@ -117,6 +119,11 @@
     // TODO: This should be removed in the future to favor using the load and store ops for discard
     virtual void discard() = 0;
 
+    /**
+     * Executes the SkDrawable object for the underlying backend.
+     */
+    virtual void executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) {}
+
 protected:
     GrGpuRTCommandBuffer() : fOrigin(kTopLeft_GrSurfaceOrigin), fRenderTarget(nullptr) {}
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index beae7f9..41a4e4a 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -26,6 +26,7 @@
 #include "GrStencilAttachment.h"
 #include "GrStyle.h"
 #include "GrTracing.h"
+#include "SkDrawable.h"
 #include "SkDrawShadowInfo.h"
 #include "SkGlyphRunPainter.h"
 #include "SkGr.h"
@@ -39,6 +40,7 @@
 #include "ops/GrClearOp.h"
 #include "ops/GrClearStencilClipOp.h"
 #include "ops/GrDebugMarkerOp.h"
+#include "ops/GrDrawableOp.h"
 #include "ops/GrDrawAtlasOp.h"
 #include "ops/GrDrawOp.h"
 #include "ops/GrDrawVerticesOp.h"
@@ -1442,6 +1444,13 @@
     this->addDrawOp(clip, std::move(op));
 }
 
+void GrRenderTargetContext::drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable,
+                                         const SkRect& bounds) {
+    std::unique_ptr<GrOp> op(GrDrawableOp::Make(fContext, std::move(drawable), bounds));
+    SkASSERT(op);
+    this->getRTOpList()->addOp(std::move(op), *this->caps());
+}
+
 GrSemaphoresSubmitted GrRenderTargetContext::prepareForExternalIO(
         int numSemaphores, GrBackendSemaphore backendSemaphores[]) {
     ASSERT_SINGLE_OWNER
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index c4a6740..6617e0b 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -17,6 +17,7 @@
 #include "GrTypesPriv.h"
 #include "GrXferProcessor.h"
 #include "SkCanvas.h"
+#include "SkDrawable.h"
 #include "SkRefCnt.h"
 #include "SkSurfaceProps.h"
 #include "text/GrTextTarget.h"
@@ -339,6 +340,12 @@
                           const SkRect& dst);
 
     /**
+     * Adds the necessary signal and wait semaphores and adds the passed in SkDrawable to the
+     * command stream.
+     */
+    void drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>, const SkRect& bounds);
+
+    /**
      * After this returns any pending surface IO will be issued to the backend 3D API and
      * if the surface has MSAA it will be resolved.
      */
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 9440013..1002053 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1696,13 +1696,14 @@
         std::unique_ptr<SkDrawable::GpuDrawHandler> gpuDraw =
                 drawable->snapGpuDrawHandler(api, combinedMatrix);
         if (gpuDraw) {
-            // TODO: send the gpuDraw to the renderTargetContext and make an Op out of it
-            // return;
+            fRenderTargetContext->drawDrawable(std::move(gpuDraw), drawable->getBounds());
+            return;
         }
     }
     this->INHERITED::drawDrawable(drawable, matrix, canvas);
 }
 
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkGpuDevice::flush() {
diff --git a/src/gpu/ops/GrDrawableOp.cpp b/src/gpu/ops/GrDrawableOp.cpp
new file mode 100644
index 0000000..6e648be
--- /dev/null
+++ b/src/gpu/ops/GrDrawableOp.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "GrDrawableOp.h"
+
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrGpuCommandBuffer.h"
+#include "GrMemoryPool.h"
+#include "GrOpFlushState.h"
+#include "SkDrawable.h"
+
+std::unique_ptr<GrDrawableOp> GrDrawableOp::Make(
+        GrContext* context, std::unique_ptr<SkDrawable::GpuDrawHandler> drawable,
+        const SkRect& bounds) {
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+    return pool->allocate<GrDrawableOp>(std::move(drawable), bounds);
+}
+
+GrDrawableOp::GrDrawableOp(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable,
+                           const SkRect& bounds)
+        : INHERITED(ClassID())
+        , fDrawable(std::move(drawable)) {
+        this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
+}
+
+void GrDrawableOp::onExecute(GrOpFlushState* state) {
+    SkASSERT(state->commandBuffer());
+    state->rtCommandBuffer()->executeDrawable(std::move(fDrawable));
+}
+
diff --git a/src/gpu/ops/GrDrawableOp.h b/src/gpu/ops/GrDrawableOp.h
new file mode 100644
index 0000000..cb295b2
--- /dev/null
+++ b/src/gpu/ops/GrDrawableOp.h
@@ -0,0 +1,49 @@
+/*
+ * 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 GrDrawableOp_DEFINED
+#define GrDrawableOp_DEFINED
+
+#include "GrOp.h"
+
+#include "GrSemaphore.h"
+#include "SkDrawable.h"
+#include "SkMatrix.h"
+
+class GrDrawableOp final : public GrOp {
+public:
+    DEFINE_OP_CLASS_ID
+
+    static std::unique_ptr<GrDrawableOp> Make(GrContext*,
+                                              std::unique_ptr<SkDrawable::GpuDrawHandler> drawable,
+                                              const SkRect& bounds);
+
+    const char* name() const override { return "Drawable"; }
+
+    SkString dumpInfo() const override {
+        return INHERITED::dumpInfo();
+    }
+
+private:
+    friend class GrOpMemoryPool; // for ctor
+
+    GrDrawableOp(std::unique_ptr<SkDrawable::GpuDrawHandler>, const SkRect& bounds);
+
+    CombineResult onCombineIfPossible(GrOp* that, const GrCaps& caps) override {
+        return CombineResult::kCannotCombine;
+    }
+    void onPrepare(GrOpFlushState*) override {}
+
+    void onExecute(GrOpFlushState* state) override;
+
+    std::unique_ptr<SkDrawable::GpuDrawHandler> fDrawable;
+
+    typedef GrOp INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index cb2875d..4675478 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -311,6 +311,8 @@
                const GrVkRenderPass* compatibleRenderPass);
     void end(const GrVkGpu* gpu);
 
+    VkCommandBuffer vkCommandBuffer() { return fCmdBuffer; }
+
 #ifdef SK_TRACE_VK_RESOURCES
     void dumpInfo() const override {
         SkDebugf("GrVkSecondaryCommandBuffer: %d (%d refs)\n", fCmdBuffer, this->getRefCnt());
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 2e6d97d..4d23f55 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -307,6 +307,9 @@
 
     fCurrentCmdBuffer->submitToQueue(this, fQueue, sync, fSemaphoresToSignal, fSemaphoresToWaitOn);
 
+    // We must delete and drawables that have been waitint till submit for us to destroy.
+    fDrawables.reset();
+
     for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) {
         fSemaphoresToWaitOn[i]->unref(this);
     }
@@ -2153,3 +2156,7 @@
     return nullptr;
 }
 
+void GrVkGpu::addDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
+    fDrawables.emplace_back(std::move(drawable));
+}
+
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 3b9a2e5..6e599de 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -141,6 +141,14 @@
     void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override;
     void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
 
+    // These match the definitions in SkDrawable, from whence they came
+    typedef void* SubmitContext;
+    typedef void (*SubmitProc)(SubmitContext submitContext);
+
+    // Adds an SkDrawable::GpuDrawHandler that we will delete the next time we submit the primary
+    // command buffer to the gpu.
+    void addDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable);
+
     sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
     void copyBuffer(GrVkBuffer* srcBuffer, GrVkBuffer* dstBuffer, VkDeviceSize srcOffset,
@@ -227,40 +235,42 @@
                                   size_t srcRowBytes, GrVkImageInfo* info);
 #endif
 
-    sk_sp<const GrVkInterface>             fInterface;
-    sk_sp<GrVkMemoryAllocator>             fMemoryAllocator;
-    sk_sp<GrVkCaps>                        fVkCaps;
+    sk_sp<const GrVkInterface>                            fInterface;
+    sk_sp<GrVkMemoryAllocator>                            fMemoryAllocator;
+    sk_sp<GrVkCaps>                                       fVkCaps;
 
-    VkInstance                             fInstance;
-    VkPhysicalDevice                       fPhysicalDevice;
-    VkDevice                               fDevice;
-    VkQueue                                fQueue;    // Must be Graphics queue
-    uint32_t                               fQueueIndex;
+    VkInstance                                            fInstance;
+    VkPhysicalDevice                                      fPhysicalDevice;
+    VkDevice                                              fDevice;
+    VkQueue                                               fQueue;    // Must be Graphics queue
+    uint32_t                                              fQueueIndex;
 
     // Created by GrVkGpu
-    GrVkResourceProvider                   fResourceProvider;
-    VkCommandPool                          fCmdPool;
+    GrVkResourceProvider                                  fResourceProvider;
+    VkCommandPool                                         fCmdPool;
 
-    GrVkPrimaryCommandBuffer*              fCurrentCmdBuffer;
+    GrVkPrimaryCommandBuffer*                             fCurrentCmdBuffer;
 
-    SkSTArray<1, GrVkSemaphore::Resource*> fSemaphoresToWaitOn;
-    SkSTArray<1, GrVkSemaphore::Resource*> fSemaphoresToSignal;
+    SkSTArray<1, GrVkSemaphore::Resource*>                fSemaphoresToWaitOn;
+    SkSTArray<1, GrVkSemaphore::Resource*>                fSemaphoresToSignal;
 
-    VkPhysicalDeviceProperties             fPhysDevProps;
-    VkPhysicalDeviceMemoryProperties       fPhysDevMemProps;
+    SkTArray<std::unique_ptr<SkDrawable::GpuDrawHandler>> fDrawables;
 
-    GrVkCopyManager                        fCopyManager;
+    VkPhysicalDeviceProperties                            fPhysDevProps;
+    VkPhysicalDeviceMemoryProperties                      fPhysDevMemProps;
+
+    GrVkCopyManager                                       fCopyManager;
 
     // compiler used for compiling sksl into spirv. We only want to create the compiler once since
     // there is significant overhead to the first compile of any compiler.
-    SkSL::Compiler*                        fCompiler;
+    SkSL::Compiler*                                       fCompiler;
 
     // We need a bool to track whether or not we've already disconnected all the gpu resources from
     // vulkan context.
-    bool                                   fDisconnected;
+    bool                                                  fDisconnected;
 
-    std::unique_ptr<GrVkGpuRTCommandBuffer>      fCachedRTCommandBuffer;
-    std::unique_ptr<GrVkGpuTextureCommandBuffer> fCachedTexCommandBuffer;
+    std::unique_ptr<GrVkGpuRTCommandBuffer>               fCachedRTCommandBuffer;
+    std::unique_ptr<GrVkGpuTextureCommandBuffer>          fCachedTexCommandBuffer;
 
     typedef GrGpu INHERITED;
 };
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index edd73fc..fce6235 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkGpuCommandBuffer.h"
 
+#include "GrBackendDrawableInfo.h"
 #include "GrFixedClip.h"
 #include "GrMesh.h"
 #include "GrOpFlushState.h"
@@ -19,7 +20,9 @@
 #include "GrVkRenderPass.h"
 #include "GrVkRenderTarget.h"
 #include "GrVkResourceProvider.h"
+#include "GrVkSemaphore.h"
 #include "GrVkTexture.h"
+#include "SkDrawable.h"
 #include "SkRect.h"
 
 void GrVkGpuTextureCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin,
@@ -267,6 +270,8 @@
     fRenderTarget = nullptr;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 void GrVkGpuRTCommandBuffer::discard() {
     GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget);
 
@@ -777,3 +782,36 @@
                                         baseIndex, baseVertex, baseInstance);
     fGpu->stats()->incNumDraws();
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrVkGpuRTCommandBuffer::executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
+    GrVkRenderTarget* target = static_cast<GrVkRenderTarget*>(fRenderTarget);
+
+    GrVkImage* targetImage = target->msaaImage() ? target->msaaImage() : target;
+
+    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
+    VkRect2D bounds;
+    bounds.offset = { 0, 0 };
+    bounds.extent = { 0, 0 };
+
+    GrVkDrawableInfo vkInfo;
+    vkInfo.fSecondaryCommandBuffer = cbInfo.currentCmdBuf()->vkCommandBuffer();
+    vkInfo.fCompatibleRenderPass = cbInfo.fRenderPass->vkRenderPass();
+    SkAssertResult(cbInfo.fRenderPass->colorAttachmentIndex(&vkInfo.fImageAttachmentIndex));
+    vkInfo.fFormat = targetImage->imageFormat();
+    vkInfo.fDrawBounds = &bounds;
+
+    GrBackendDrawableInfo info(vkInfo);
+
+    drawable->draw(info);
+    fGpu->addDrawable(std::move(drawable));
+
+    if (bounds.extent.width == 0 || bounds.extent.height == 0) {
+        cbInfo.fBounds.join(target->getBoundsRect());
+    } else {
+        cbInfo.fBounds.join(SkRect::MakeXYWH(bounds.offset.x, bounds.offset.y,
+                                             bounds.extent.width, bounds.extent.height));
+    }
+}
+
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index 6aad0c2..b40cff3 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -74,6 +74,8 @@
     void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
               const SkIPoint& dstPoint) override;
 
+    void executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) override;
+
     void set(GrRenderTarget*, GrSurfaceOrigin,
              const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
              const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&);
diff --git a/tests/VkDrawableTest.cpp b/tests/VkDrawableTest.cpp
new file mode 100644
index 0000000..ff24708
--- /dev/null
+++ b/tests/VkDrawableTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU && defined(SK_VULKAN)
+
+#include "GrBackendDrawableInfo.h"
+#include "GrContextFactory.h"
+#include "GrContextPriv.h"
+#include "SkDrawable.h"
+#include "SkSurface.h"
+#include "Test.h"
+#include "vk/GrVkGpu.h"
+#include "vk/GrVkInterface.h"
+#include "vk/GrVkMemory.h"
+#include "vk/GrVkUtil.h"
+
+using sk_gpu_test::GrContextFactory;
+
+static const int DEV_W = 16, DEV_H = 16;
+
+class TestDrawable : public SkDrawable {
+public:
+    TestDrawable(const GrVkInterface* interface, int32_t width, int32_t height)
+            : INHERITED()
+            , fInterface(interface)
+            , fWidth(width)
+            , fHeight(height) {}
+
+    ~TestDrawable() override {}
+
+    class DrawHandler : public GpuDrawHandler {
+    public:
+        DrawHandler(const GrVkInterface* interface, int32_t width, int32_t height)
+            : INHERITED()
+            , fInterface(interface)
+            , fWidth(width)
+            , fHeight(height) {}
+        ~DrawHandler() override {}
+
+        void draw(const GrBackendDrawableInfo& info) override {
+            GrVkDrawableInfo vkInfo;
+            SkAssertResult(info.getVkDrawableInfo(&vkInfo));
+
+            // Clear to Red
+            VkClearColorValue vkColor;
+            vkColor.float32[0] = 1.0f; // r
+            vkColor.float32[1] = 0.0f; // g
+            vkColor.float32[2] = 0.0f; // b
+            vkColor.float32[3] = 1.0f; // a
+
+            // Clear right half of render target
+            VkClearRect clearRect;
+            clearRect.rect.offset = { fWidth / 2, 0 };
+            clearRect.rect.extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
+            clearRect.baseArrayLayer = 0;
+            clearRect.layerCount = 1;
+
+            VkClearAttachment attachment;
+            attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+            attachment.colorAttachment = vkInfo.fImageAttachmentIndex;
+            attachment.clearValue.color = vkColor;
+
+            GR_VK_CALL(fInterface, CmdClearAttachments(vkInfo.fSecondaryCommandBuffer,
+                                                       1,
+                                                       &attachment,
+                                                       1,
+                                                       &clearRect));
+            vkInfo.fDrawBounds->offset = { fWidth / 2, 0 };
+            vkInfo.fDrawBounds->extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
+        }
+    private:
+        const GrVkInterface* fInterface;
+        int32_t              fWidth;
+        int32_t              fHeight;
+
+        typedef GpuDrawHandler INHERITED;
+    };
+
+    std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
+                                                         const SkMatrix& matrix) override {
+        if (backendApi != GrBackendApi::kVulkan) {
+            return nullptr;
+        }
+        std::unique_ptr<DrawHandler> draw(new DrawHandler(fInterface, fWidth, fHeight));
+        return std::move(draw);
+    }
+
+    SkRect onGetBounds() override {
+        return SkRect::MakeLTRB(fWidth / 2, 0, fWidth, fHeight);
+    }
+
+    void onDraw(SkCanvas*) override {
+        SkASSERT(false);
+    }
+
+private:
+    const GrVkInterface* fInterface;
+    int32_t fWidth;
+    int32_t fHeight;
+
+    typedef SkDrawable INHERITED;
+};
+
+void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context) {
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu());
+
+    const SkImageInfo ii = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType,
+                                             kPremul_SkAlphaType);
+    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo,
+                                                         ii, 0, kTopLeft_GrSurfaceOrigin, nullptr));
+    SkCanvas* canvas = surface->getCanvas();
+    canvas->clear(SK_ColorBLUE);
+
+    sk_sp<TestDrawable> drawable(new TestDrawable(gpu->vkInterface(), DEV_W, DEV_H));
+    canvas->drawDrawable(drawable.get());
+
+    SkPaint paint;
+    paint.setColor(SK_ColorGREEN);
+    SkIRect rect = SkIRect::MakeLTRB(0, DEV_H/2, DEV_W, DEV_H);
+    canvas->drawIRect(rect, paint);
+
+    // read pixels
+    SkBitmap bitmap;
+    bitmap.allocPixels(ii);
+    canvas->readPixels(bitmap, 0, 0);
+
+    const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
+    bool failureFound = false;
+    SkPMColor expectedPixel;
+    for (int cy = 0; cy < DEV_H || failureFound; ++cy) {
+        for (int cx = 0; cx < DEV_W || failureFound; ++cx) {
+            SkPMColor canvasPixel = canvasPixels[cy * DEV_W + cx];
+            if (cy < DEV_H / 2) {
+                if (cx < DEV_W / 2) {
+                    expectedPixel = 0xFFFF0000; // Blue
+                } else {
+                    expectedPixel = 0xFF0000FF; // Red
+                }
+            } else {
+                expectedPixel = 0xFF00FF00; // Green
+            }
+            if (expectedPixel != canvasPixel) {
+                failureFound = true;
+                ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x",
+                       cx, cy, canvasPixel, expectedPixel);
+            }
+        }
+    }
+}
+
+
+DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkDrawableTest, reporter, ctxInfo) {
+    draw_drawable_test(reporter, ctxInfo.grContext());
+}
+
+#endif