Add API for flushing surfaces with gpu semaphores

BUG=skia:

Change-Id: Ia4bfef784cd5f2516ceccafce958be18a86f91d1
Reviewed-on: https://skia-review.googlesource.com/11488
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Forrest Reiling <freiling@google.com>
diff --git a/dm/DM.cpp b/dm/DM.cpp
index d92b5ae..58ccb1f 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -1432,36 +1432,6 @@
 bool IsNullGLContextType(sk_gpu_test::GrContextFactory::ContextType type) {
     return type == GrContextFactory::kNullGL_ContextType;
 }
-const char* ContextTypeName(GrContextFactory::ContextType contextType) {
-    switch (contextType) {
-        case GrContextFactory::kGL_ContextType:
-            return "OpenGL";
-        case GrContextFactory::kGLES_ContextType:
-            return "OpenGLES";
-        case GrContextFactory::kANGLE_D3D9_ES2_ContextType:
-            return "ANGLE D3D9 ES2";
-        case GrContextFactory::kANGLE_D3D11_ES2_ContextType:
-            return "ANGLE D3D11 ES2";
-        case GrContextFactory::kANGLE_D3D11_ES3_ContextType:
-            return "ANGLE D3D11 ES3";
-        case GrContextFactory::kANGLE_GL_ES2_ContextType:
-            return "ANGLE GL ES2";
-        case GrContextFactory::kANGLE_GL_ES3_ContextType:
-            return "ANGLE GL ES3";
-        case GrContextFactory::kCommandBuffer_ContextType:
-            return "Command Buffer";
-        case GrContextFactory::kMESA_ContextType:
-            return "Mesa";
-        case GrContextFactory::kNullGL_ContextType:
-            return "Null GL";
-        case GrContextFactory::kDebugGL_ContextType:
-            return "Debug GL";
-        case GrContextFactory::kVulkan_ContextType:
-            return "Vulkan";
-    }
-    SkDEBUGFAIL("Unreachable");
-    return "Unknown";
-}
 #else
 bool IsGLContextType(int) { return false; }
 bool IsVulkanContextType(int) { return false; }
@@ -1494,7 +1464,7 @@
         if (contextTypeFilter && !(*contextTypeFilter)(contextType)) {
             continue;
         }
-        ReporterContext ctx(reporter, SkString(ContextTypeName(contextType)));
+        ReporterContext ctx(reporter, SkString(GrContextFactory::ContextTypeName(contextType)));
         if (ctxInfo.grContext()) {
             (*test)(reporter, ctxInfo);
         }
diff --git a/gn/gpu.gni b/gn/gpu.gni
index fab5d97..2547981 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -8,6 +8,7 @@
 _include = get_path_info("../include", "abspath")
 
 skia_gpu_sources = [
+  "$_include/gpu/GrBackendSemaphore.h",
   "$_include/gpu/GrBackendSurface.h",
   "$_include/gpu/GrBlend.h",
   "$_include/gpu/GrCaps.h",
diff --git a/gn/tests.gni b/gn/tests.gni
index f961c84..80af714 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -232,6 +232,7 @@
   "$_tests/StrokerTest.cpp",
   "$_tests/StrokeTest.cpp",
   "$_tests/SubsetPath.cpp",
+  "$_tests/SurfaceSemaphoreTest.cpp",
   "$_tests/SurfaceTest.cpp",
   "$_tests/SVGDeviceTest.cpp",
   "$_tests/SwizzlerTest.cpp",
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index efa2428..6fad217 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -15,6 +15,7 @@
 class SkCanvas;
 class SkPaint;
 class GrBackendRenderTarget;
+class GrBackendSemaphore;
 class GrContext;
 class GrRenderTarget;
 
@@ -323,9 +324,33 @@
 
     /**
      * Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA.
+     *
+     * The flush calls below are the new preferred way to flush calls to a surface, and this call
+     * will eventually be removed.
      */
     void prepareForExternalIO();
 
+    /**
+     * Issue any pending surface IO to the current backend 3D API
+     */
+    void flush();
+
+    /**
+     * Issue any pending surface IO to the current backend 3D API. After issuing all commands, we
+     * will issue numSemaphore semaphores for the gpu to signal. We will then fill in the array
+     * signalSemaphores with the info on the semaphores we submitted. The client is reposonsible for
+     * allocating enough space in signalSemaphores to handle numSemaphores of GrBackendSemaphores.
+     * The client will also take ownership of the returned underlying backend semaphores.
+     */
+    void flushAndSignalSemaphores(int numSemaphores, GrBackendSemaphore* signalSemaphores);
+
+    /**
+     * Inserts a list of GPU semaphores that the current backend 3D API must wait on before
+     * executing any more commands on the GPU for this surface. Skia will take ownership of the
+     * underlying semaphores and delete them once they have been signaled and waited on.
+     */
+    void wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores);
+
 protected:
     SkSurface(int width, int height, const SkSurfaceProps*);
     SkSurface(const SkImageInfo&, const SkSurfaceProps*);
diff --git a/include/gpu/GrBackendSemaphore.h b/include/gpu/GrBackendSemaphore.h
new file mode 100644
index 0000000..668f6bb
--- /dev/null
+++ b/include/gpu/GrBackendSemaphore.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrBackendSemaphore_DEFINED
+#define GrBackendSemaphore_DEFINED
+
+#include "GrTypes.h"
+
+#include "gl/GrGLTypes.h"
+
+#ifdef SK_VULKAN
+#include "vk/GrVkTypes.h"
+#endif
+
+/**
+ * Wrapper class for passing into and receiving data from Ganesh about a backend semaphore object.
+ */
+class GrBackendSemaphore {
+public:
+    // For convenience we just set the backend here to OpenGL. The GrBackendSemaphore cannot be used
+    // until either initGL or initVulkan are called which will set the appropriate GrBackend.
+    GrBackendSemaphore() : fBackend(kOpenGL_GrBackend), fGLSync(0), fIsInitialized(false) {}
+
+    void initGL(GrGLsync sync) {
+        fBackend = kOpenGL_GrBackend;
+        fGLSync = sync;
+        fIsInitialized = true;
+    }
+
+#ifdef SK_VULKAN
+    void initVulkan(VkSemaphore semaphore) {
+        fBackend = kVulkan_GrBackend;
+        fVkSemaphore = semaphore;
+        fIsInitialized = true;
+    }
+#endif
+
+    GrGLsync glSync() const {
+        if (!fIsInitialized || kOpenGL_GrBackend != fBackend) {
+            return 0;
+        }
+        return fGLSync;
+    }
+
+#ifdef SK_VULKAN
+    VkSemaphore vkSemaphore() const {
+        if (!fIsInitialized || kVulkan_GrBackend != fBackend) {
+            return VK_NULL_HANDLE;
+        }
+        return fVkSemaphore;
+    }
+#endif
+
+private:
+    GrBackend fBackend;
+    union {
+        GrGLsync    fGLSync;
+#ifdef SK_VULKAN
+        VkSemaphore fVkSemaphore;
+#endif
+    };
+    bool fIsInitialized;
+};
+
+#endif
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 3c400a8..418b8b4 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -21,6 +21,7 @@
 #include <map>
 
 class GrBackendRenderTarget;
+class GrBackendSemaphore;
 class GrBuffer;
 class GrContext;
 struct GrContextOptions;
@@ -376,7 +377,9 @@
     virtual bool waitFence(GrFence, uint64_t timeout = 1000) = 0;
     virtual void deleteFence(GrFence) const = 0;
 
-    virtual sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() = 0;
+    virtual sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true) = 0;
+    virtual sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+                                                    GrWrapOwnership ownership) = 0;
     virtual void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush = false) = 0;
     virtual void waitSemaphore(sk_sp<GrSemaphore> semaphore) = 0;
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index c5a115f..17a7db8 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -9,6 +9,7 @@
 #include "../private/GrAuditTrail.h"
 #include "../private/SkShadowFlags.h"
 #include "GrAppliedClip.h"
+#include "GrBackendSemaphore.h"
 #include "GrColor.h"
 #include "GrContextPriv.h"
 #include "GrDrawingManager.h"
@@ -38,6 +39,7 @@
 #include "ops/GrOvalOpFactory.h"
 #include "ops/GrRectOpFactory.h"
 #include "ops/GrRegionOp.h"
+#include "ops/GrSemaphoreOp.h"
 #include "ops/GrShadowRRectOp.h"
 #include "ops/GrStencilPathOp.h"
 #include "text/GrAtlasTextContext.h"
@@ -508,7 +510,7 @@
 
     const SkStrokeRec& stroke = style->strokeRec();
     if (stroke.getStyle() == SkStrokeRec::kFill_Style) {
-        
+
         if (!fContext->caps()->useDrawInsteadOfClear()) {
             // Check if this is a full RT draw and can be replaced with a clear. We don't bother
             // checking cases where the RT is fully inside a stroke.
@@ -1429,13 +1431,44 @@
     this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
 }
 
-void GrRenderTargetContext::prepareForExternalIO() {
+void GrRenderTargetContext::prepareForExternalIO(int numSemaphores,
+                                                 GrBackendSemaphore* backendSemaphores) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::prepareForExternalIO");
 
+    SkTArray<sk_sp<GrSemaphore>> semaphores(numSemaphores);
+    for (int i = 0; i < numSemaphores; ++i) {
+        semaphores.push_back(fContext->resourceProvider()->makeSemaphore(false));
+        std::unique_ptr<GrOp> signalOp(GrSemaphoreOp::MakeSignal(semaphores.back(),
+                                                                 fRenderTargetProxy.get()));
+        this->getOpList()->addOp(std::move(signalOp), *this->caps());
+    }
+
     this->drawingManager()->prepareSurfaceForExternalIO(fRenderTargetProxy.get());
+
+    for (int i = 0; i < numSemaphores; ++i) {
+        semaphores[i]->setBackendSemaphore(&backendSemaphores[i]);
+    }
+}
+
+void GrRenderTargetContext::waitOnSemaphores(int numSemaphores,
+                                             const GrBackendSemaphore* waitSemaphores) {
+    ASSERT_SINGLE_OWNER
+    RETURN_IF_ABANDONED
+    SkDEBUGCODE(this->validate();)
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::waitOnSemaphores");
+
+    AutoCheckFlush acf(this->drawingManager());
+
+    SkTArray<sk_sp<GrSemaphore>> semaphores(numSemaphores);
+    for (int i = 0; i < numSemaphores; ++i) {
+        sk_sp<GrSemaphore> sema = fContext->resourceProvider()->wrapBackendSemaphore(
+                waitSemaphores[i], kAdopt_GrWrapOwnership);
+        std::unique_ptr<GrOp> waitOp(GrSemaphoreOp::MakeWait(sema, fRenderTargetProxy.get()));
+        this->getOpList()->addOp(std::move(waitOp), *this->caps());
+    }
 }
 
 // Can 'path' be drawn as a pair of filled nested rectangles?
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index bc22604..a2369dd 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -19,6 +19,7 @@
 #include "SkRefCnt.h"
 #include "SkSurfaceProps.h"
 
+class GrBackendSemaphore;
 class GrClip;
 class GrDrawingManager;
 class GrDrawOp;
@@ -302,7 +303,13 @@
      * 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.
      */
-    void prepareForExternalIO();
+    void prepareForExternalIO(int numSemaphores, GrBackendSemaphore* backendSemaphores);
+
+    /**
+     *  The next time this GrRenderTargetContext is flushed, the gpu will wait on the passed in
+     *  semaphores before executing any commands.
+     */
+    void waitOnSemaphores(int numSemaphores, const GrBackendSemaphore* waitSemaphores);
 
     GrFSAAType fsaaType() const { return fRenderTargetProxy->fsaaType(); }
     const GrCaps* caps() const { return fContext->caps(); }
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 52340bb..dccfaae 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -7,6 +7,7 @@
 
 #include "GrResourceProvider.h"
 
+#include "GrBackendSemaphore.h"
 #include "GrBuffer.h"
 #include "GrCaps.h"
 #include "GrContext.h"
@@ -505,8 +506,14 @@
     return this->gpu()->wrapBackendTextureAsRenderTarget(tex, origin, sampleCnt);
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore() {
-    return fGpu->makeSemaphore();
+sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore(bool isOwned) {
+    return fGpu->makeSemaphore(isOwned);
+}
+
+sk_sp<GrSemaphore> GrResourceProvider::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+                                                            GrWrapOwnership ownership) {
+    ASSERT_SINGLE_OWNER
+    return this->isAbandoned() ? nullptr : fGpu->wrapBackendSemaphore(semaphore, ownership);
 }
 
 void GrResourceProvider::takeOwnershipOfSemaphore(sk_sp<GrSemaphore> semaphore) {
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 50264ef..a6ddbc5 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -8,13 +8,14 @@
 #ifndef GrResourceProvider_DEFINED
 #define GrResourceProvider_DEFINED
 
-#include "GrBackendSurface.h"
 #include "GrBuffer.h"
 #include "GrPathRange.h"
 #include "SkImageInfo.h"
 #include "SkScalerContext.h"
 
 class GrBackendRenderTarget;
+class GrBackendSemaphore;
+class GrBackendTexture;
 class GrGpu;
 class GrPath;
 class GrRenderTarget;
@@ -227,7 +228,10 @@
      */
     GrGpuResource* findAndRefResourceByUniqueKey(const GrUniqueKey&);
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore();
+    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true);
+
+    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore&,
+                                            GrWrapOwnership = kBorrow_GrWrapOwnership);
 
     // Takes the GrSemaphore and sets the ownership of the semaphore to the GrGpu object used by
     // this class. This call is only used when passing a GrSemaphore from one context to another.
diff --git a/src/gpu/GrSemaphore.h b/src/gpu/GrSemaphore.h
index b4843ff..f6148b0 100644
--- a/src/gpu/GrSemaphore.h
+++ b/src/gpu/GrSemaphore.h
@@ -10,6 +10,7 @@
 
 #include "SkRefCnt.h"
 
+class GrBackendSemaphore;
 class GrGpu;
 
 class GrSemaphore : public SkRefCnt {
@@ -20,9 +21,15 @@
     // GrSemaphore should not be used with its old context.
     void resetGpu(const GrGpu* gpu) { fGpu = gpu; }
 
+    // The derived class will init the GrBackendSemaphore. This is used when flushing with signal
+    // semaphores so we can set the clients GrBackendSemaphore object after we've created the
+    // internal semaphore.
+    virtual void setBackendSemaphore(GrBackendSemaphore*) const = 0;
+
 protected:
     explicit GrSemaphore(const GrGpu* gpu) : fGpu(gpu) {}
 
+    friend class GrRenderTargetContext; // setBackendSemaphore
     friend class GrResourceProvider; // resetGpu
 
     const GrGpu* fGpu;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index be842ba..ee6e88d 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1730,10 +1730,23 @@
     return GrTextUtils::ShouldDisableLCD(paint);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
 void SkGpuDevice::flush() {
+    this->flushAndSignalSemaphores(0, nullptr);
+}
+
+void SkGpuDevice::flushAndSignalSemaphores(int numSemaphores,
+                                           GrBackendSemaphore* signalSemaphores) {
     ASSERT_SINGLE_OWNER
 
-    fRenderTargetContext->prepareForExternalIO();
+    fRenderTargetContext->prepareForExternalIO(numSemaphores, signalSemaphores);
+}
+
+void SkGpuDevice::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
+    ASSERT_SINGLE_OWNER
+
+    fRenderTargetContext->waitOnSemaphores(numSemaphores, waitSemaphores);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 0b60a96..317533d 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -118,6 +118,8 @@
     sk_sp<SkSpecialImage> snapSpecial() override;
 
     void flush() override;
+    void flushAndSignalSemaphores(int numSemaphores, GrBackendSemaphore* signalSemaphores);
+    void wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores);
 
     bool onAccessPixels(SkPixmap*) override;
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index b3f8c2c..d401600 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -9,6 +9,7 @@
 
 #include <cmath>
 #include "../private/GrGLSL.h"
+#include "GrBackendSemaphore.h"
 #include "GrBackendSurface.h"
 #include "GrFixedClip.h"
 #include "GrGLBuffer.h"
@@ -4288,10 +4289,16 @@
     this->deleteSync((GrGLsync)fence);
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrGLGpu::makeSemaphore() {
-    return GrGLSemaphore::Make(this);
+sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrGLGpu::makeSemaphore(bool isOwned) {
+    return GrGLSemaphore::Make(this, isOwned);
 }
 
+sk_sp<GrSemaphore> GrGLGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+                                                 GrWrapOwnership ownership) {
+    return GrGLSemaphore::MakeWrapped(this, semaphore.glSync(), ownership);
+}
+
+
 void GrGLGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) {
     GrGLSemaphore* glSem = static_cast<GrGLSemaphore*>(semaphore.get());
 
@@ -4316,7 +4323,7 @@
 
 sk_sp<GrSemaphore> GrGLGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
     // Set up a semaphore to be signaled once the data is ready, and flush GL
-    sk_sp<GrSemaphore> semaphore = this->makeSemaphore();
+    sk_sp<GrSemaphore> semaphore = this->makeSemaphore(true);
     this->insertSemaphore(semaphore, true);
 
     return semaphore;
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index f121df3..80a12eb 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -166,7 +166,9 @@
     bool waitFence(GrFence, uint64_t timeout) override;
     void deleteFence(GrFence) const override;
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override;
+    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
+    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+                                            GrWrapOwnership ownership) override;
     void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override;
     void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
 
diff --git a/src/gpu/gl/GrGLSemaphore.h b/src/gpu/gl/GrGLSemaphore.h
index f439ebd..cfc3de9 100644
--- a/src/gpu/gl/GrGLSemaphore.h
+++ b/src/gpu/gl/GrGLSemaphore.h
@@ -10,16 +10,26 @@
 
 #include "GrSemaphore.h"
 
-class GrGLGpu;
+#include "GrBackendSemaphore.h"
+#include "GrGLGpu.h"
 
 class GrGLSemaphore : public GrSemaphore {
 public:
-    static sk_sp<GrGLSemaphore> Make(const GrGLGpu* gpu) {
-        return sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu));
+    static sk_sp<GrGLSemaphore> Make(const GrGLGpu* gpu, bool isOwned) {
+        return sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu, isOwned));
+    }
+
+    static sk_sp<GrGLSemaphore> MakeWrapped(const GrGLGpu* gpu,
+                                            GrGLsync sync,
+                                            GrWrapOwnership ownership) {
+        auto sema = sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu,
+                                                           kBorrow_GrWrapOwnership != ownership));
+        sema->setSync(sync);
+        return sema;
     }
 
     ~GrGLSemaphore() override {
-        if (fGpu) {
+        if (fIsOwned && fGpu) {
             static_cast<const GrGLGpu*>(fGpu)->deleteSync(fSync);
         }
     }
@@ -28,9 +38,14 @@
     void setSync(const GrGLsync& sync) { fSync = sync; }
 
 private:
-    GrGLSemaphore(const GrGLGpu* gpu) : INHERITED(gpu), fSync(0) {}
+    GrGLSemaphore(const GrGLGpu* gpu, bool isOwned) : INHERITED(gpu), fSync(0), fIsOwned(isOwned) {}
+
+    void setBackendSemaphore(GrBackendSemaphore* backendSemaphore) const override {
+        backendSemaphore->initGL(fSync);
+    }
 
     GrGLsync fSync;
+    bool     fIsOwned;
 
     typedef GrSemaphore INHERITED;
 };
diff --git a/src/gpu/ops/GrSemaphoreOp.cpp b/src/gpu/ops/GrSemaphoreOp.cpp
index e83096d..f50d9c0 100644
--- a/src/gpu/ops/GrSemaphoreOp.cpp
+++ b/src/gpu/ops/GrSemaphoreOp.cpp
@@ -14,15 +14,17 @@
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrSignalSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore) {
-        return std::unique_ptr<GrSignalSemaphoreOp>(new GrSignalSemaphoreOp(std::move(semaphore)));
+    static std::unique_ptr<GrSignalSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore,
+                                                     GrRenderTargetProxy* proxy) {
+        return std::unique_ptr<GrSignalSemaphoreOp>(new GrSignalSemaphoreOp(std::move(semaphore),
+                                                                            proxy));
     }
 
     const char* name() const override { return "SignalSemaphore"; }
 
 private:
-    explicit GrSignalSemaphoreOp(sk_sp<GrSemaphore> semaphore)
-            : INHERITED(ClassID(), std::move(semaphore)) {}
+    explicit GrSignalSemaphoreOp(sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy)
+            : INHERITED(ClassID(), std::move(semaphore), proxy) {}
 
     void onExecute(GrOpFlushState* state) override {
         state->gpu()->insertSemaphore(fSemaphore);
@@ -35,15 +37,17 @@
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrWaitSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore) {
-        return std::unique_ptr<GrWaitSemaphoreOp>(new GrWaitSemaphoreOp(std::move(semaphore)));
+    static std::unique_ptr<GrWaitSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore,
+                                                   GrRenderTargetProxy* proxy) {
+        return std::unique_ptr<GrWaitSemaphoreOp>(new GrWaitSemaphoreOp(std::move(semaphore),
+                                                                        proxy));
     }
 
     const char* name() const override { return "WaitSemaphore"; }
 
 private:
-    explicit GrWaitSemaphoreOp(sk_sp<GrSemaphore> semaphore)
-            : INHERITED(ClassID(), std::move(semaphore)) {}
+    explicit GrWaitSemaphoreOp(sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy)
+            : INHERITED(ClassID(), std::move(semaphore), proxy) {}
 
     void onExecute(GrOpFlushState* state) override {
         state->gpu()->waitSemaphore(fSemaphore);
@@ -54,12 +58,14 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeSignal(sk_sp<GrSemaphore> semaphore) {
-    return GrSignalSemaphoreOp::Make(std::move(semaphore));
+std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeSignal(sk_sp<GrSemaphore> semaphore,
+                                                         GrRenderTargetProxy* proxy) {
+    return GrSignalSemaphoreOp::Make(std::move(semaphore), proxy);
 }
 
-std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeWait(sk_sp<GrSemaphore> semaphore) {
-    return GrWaitSemaphoreOp::Make(std::move(semaphore));
+std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeWait(sk_sp<GrSemaphore> semaphore,
+                                                       GrRenderTargetProxy* proxy) {
+    return GrWaitSemaphoreOp::Make(std::move(semaphore), proxy);
 }
 
 
diff --git a/src/gpu/ops/GrSemaphoreOp.h b/src/gpu/ops/GrSemaphoreOp.h
index a88b66c..af9566a 100644
--- a/src/gpu/ops/GrSemaphoreOp.h
+++ b/src/gpu/ops/GrSemaphoreOp.h
@@ -10,18 +10,24 @@
 
 #include "GrOp.h"
 
+#include "GrRenderTargetProxy.h"
 #include "GrSemaphore.h"
 #include "SkRefCnt.h"
 
 class GrSemaphoreOp : public GrOp {
 public:
-    static std::unique_ptr<GrSemaphoreOp> MakeSignal(sk_sp<GrSemaphore> semaphore);
+    static std::unique_ptr<GrSemaphoreOp> MakeSignal(sk_sp<GrSemaphore> semaphore,
+                                                     GrRenderTargetProxy* proxy);
 
-    static std::unique_ptr<GrSemaphoreOp> MakeWait(sk_sp<GrSemaphore> semaphore);
+    static std::unique_ptr<GrSemaphoreOp> MakeWait(sk_sp<GrSemaphore> semaphore,
+                                                   GrRenderTargetProxy* proxy);
 
 protected:
-    GrSemaphoreOp(uint32_t classId, sk_sp<GrSemaphore> semaphore)
-        : INHERITED(classId), fSemaphore(std::move(semaphore)) {}
+    GrSemaphoreOp(uint32_t classId, sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy)
+        : INHERITED(classId), fSemaphore(std::move(semaphore)) {
+        this->setBounds(SkRect::MakeIWH(proxy->width(), proxy->height()),
+                        HasAABloat::kNo, IsZeroArea::kNo);
+    }
 
     sk_sp<GrSemaphore> fSemaphore;
 
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index ea0a02c..63642d4 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -402,7 +402,7 @@
         const GrVkGpu* gpu,
         VkQueue queue,
         GrVkGpu::SyncQueue sync,
-        const GrVkSemaphore::Resource* signalSemaphore,
+        SkTArray<const GrVkSemaphore::Resource*>& signalSemaphores,
         SkTArray<const GrVkSemaphore::Resource*>& waitSemaphores) {
     SkASSERT(!fIsActive);
 
@@ -418,23 +418,20 @@
         GR_VK_CALL(gpu->vkInterface(), ResetFences(gpu->device(), 1, &fSubmitFence));
     }
 
-    if (signalSemaphore) {
-        this->addResource(signalSemaphore);
+    int signalCount = signalSemaphores.count();
+    SkTArray<VkSemaphore> vkSignalSem(signalCount);
+    for (int i = 0; i < signalCount; ++i) {
+        this->addResource(signalSemaphores[i]);
+        vkSignalSem.push_back(signalSemaphores[i]->semaphore());
     }
 
     int waitCount = waitSemaphores.count();
     SkTArray<VkSemaphore> vkWaitSems(waitCount);
     SkTArray<VkPipelineStageFlags> vkWaitStages(waitCount);
-    if (waitCount) {
-        for (int i = 0; i < waitCount; ++i) {
-            this->addResource(waitSemaphores[i]);
-            vkWaitSems.push_back(waitSemaphores[i]->semaphore());
-            vkWaitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
-        }
-    }
-    SkTArray<VkSemaphore> vkSignalSem;
-    if (signalSemaphore) {
-        vkSignalSem.push_back(signalSemaphore->semaphore());
+    for (int i = 0; i < waitCount; ++i) {
+        this->addResource(waitSemaphores[i]);
+        vkWaitSems.push_back(waitSemaphores[i]->semaphore());
+        vkWaitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
     }
 
     VkSubmitInfo submitInfo;
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index 1f3c4a5..b387885 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -298,7 +298,7 @@
                       const VkImageResolve* regions);
 
     void submitToQueue(const GrVkGpu* gpu, VkQueue queue, GrVkGpu::SyncQueue sync,
-                       const GrVkSemaphore::Resource* signalSemaphore,
+                       SkTArray<const GrVkSemaphore::Resource*>& signalSemaphores,
                        SkTArray<const GrVkSemaphore::Resource*>& waitSemaphores);
     bool finished(const GrVkGpu* gpu) const;
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index f76f06c..b10e9ed 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkGpu.h"
 
+#include "GrBackendSemaphore.h"
 #include "GrBackendSurface.h"
 #include "GrContextOptions.h"
 #include "GrGeometryProcessor.h"
@@ -191,6 +192,12 @@
     }
     fSemaphoresToWaitOn.reset();
 
+    for (int i = 0; i < fSemaphoresToSignal.count(); ++i) {
+        fSemaphoresToSignal[i]->unref(this);
+    }
+    fSemaphoresToSignal.reset();
+
+
     fCopyManager.destroyResources(this);
 
     // must call this just before we destroy the command pool and VkDevice
@@ -226,12 +233,16 @@
             for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) {
                 fSemaphoresToWaitOn[i]->unrefAndAbandon();
             }
+            for (int i = 0; i < fSemaphoresToSignal.count(); ++i) {
+                fSemaphoresToSignal[i]->unrefAndAbandon();
+            }
             fCopyManager.abandonResources();
 
             // must call this just before we destroy the command pool and VkDevice
             fResourceProvider.abandonResources();
         }
         fSemaphoresToWaitOn.reset();
+        fSemaphoresToSignal.reset();
 #ifdef SK_ENABLE_VK_LAYERS
         fCallback = VK_NULL_HANDLE;
 #endif
@@ -249,17 +260,20 @@
     return new GrVkGpuCommandBuffer(this, colorInfo, stencilInfo);
 }
 
-void GrVkGpu::submitCommandBuffer(SyncQueue sync,
-                                  const GrVkSemaphore::Resource* signalSemaphore) {
+void GrVkGpu::submitCommandBuffer(SyncQueue sync) {
     SkASSERT(fCurrentCmdBuffer);
     fCurrentCmdBuffer->end(this);
 
-    fCurrentCmdBuffer->submitToQueue(this, fQueue, sync, signalSemaphore, fSemaphoresToWaitOn);
+    fCurrentCmdBuffer->submitToQueue(this, fQueue, sync, fSemaphoresToSignal, fSemaphoresToWaitOn);
 
     for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) {
         fSemaphoresToWaitOn[i]->unref(this);
     }
     fSemaphoresToWaitOn.reset();
+    for (int i = 0; i < fSemaphoresToSignal.count(); ++i) {
+        fSemaphoresToSignal[i]->unref(this);
+    }
+    fSemaphoresToSignal.reset();
 
     fResourceProvider.checkCommandBuffers();
 
@@ -1937,15 +1951,25 @@
     VK_CALL(DestroyFence(this->device(), (VkFence)fence, nullptr));
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrVkGpu::makeSemaphore() {
-    return GrVkSemaphore::Make(this);
+sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrVkGpu::makeSemaphore(bool isOwned) {
+    return GrVkSemaphore::Make(this, isOwned);
 }
 
-void GrVkGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool /*flush*/) {
+sk_sp<GrSemaphore> GrVkGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+                                                 GrWrapOwnership ownership) {
+    return GrVkSemaphore::MakeWrapped(this, semaphore.vkSemaphore(), ownership);
+}
+
+void GrVkGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) {
     GrVkSemaphore* vkSem = static_cast<GrVkSemaphore*>(semaphore.get());
 
-    // We *always* flush, so ignore that parameter
-    this->submitCommandBuffer(kSkip_SyncQueue, vkSem->getResource());
+    const GrVkSemaphore::Resource* resource = vkSem->getResource();
+    resource->ref();
+    fSemaphoresToSignal.push_back(resource);
+
+    if (flush) {
+        this->submitCommandBuffer(kSkip_SyncQueue);
+    }
 }
 
 void GrVkGpu::waitSemaphore(sk_sp<GrSemaphore> semaphore) {
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index f81daea..236b34a 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -131,7 +131,9 @@
     bool waitFence(GrFence, uint64_t timeout) override;
     void deleteFence(GrFence) const override;
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override;
+    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
+    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+                                            GrWrapOwnership ownership) override;
     void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override;
     void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
 
@@ -208,12 +210,11 @@
 
     // Ends and submits the current command buffer to the queue and then creates a new command
     // buffer and begins it. If sync is set to kForce_SyncQueue, the function will wait for all
-    // work in the queue to finish before returning. If the signalSemaphore is not VK_NULL_HANDLE,
-    // we will signal the semaphore at the end of this command buffer. If this GrVkGpu object has
-    // any semaphores in fSemaphoresToWaitOn, we will add those wait semaphores to this command
-    // buffer when submitting.
-    void submitCommandBuffer(SyncQueue sync,
-                             const GrVkSemaphore::Resource* signalSemaphore = nullptr);
+    // work in the queue to finish before returning. If this GrVkGpu object has any semaphores in
+    // fSemaphoreToSignal, we will add those signal semaphores to the submission of this command
+    // buffer. If this GrVkGpu object has any semaphores in fSemaphoresToWaitOn, we will add those
+    // wait semaphores to the submission of this command buffer.
+    void submitCommandBuffer(SyncQueue sync);
 
     void internalResolveRenderTarget(GrRenderTarget* target, bool requiresSubmit);
 
@@ -267,6 +268,7 @@
     GrVkPrimaryCommandBuffer*                    fCurrentCmdBuffer;
 
     SkSTArray<1, const GrVkSemaphore::Resource*> fSemaphoresToWaitOn;
+    SkSTArray<1, const GrVkSemaphore::Resource*> fSemaphoresToSignal;
 
     VkPhysicalDeviceMemoryProperties             fPhysDevMemProps;
 
diff --git a/src/gpu/vk/GrVkSemaphore.cpp b/src/gpu/vk/GrVkSemaphore.cpp
index d84635f..d201458 100644
--- a/src/gpu/vk/GrVkSemaphore.cpp
+++ b/src/gpu/vk/GrVkSemaphore.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkSemaphore.h"
 
+#include "GrBackendSemaphore.h"
 #include "GrVkGpu.h"
 #include "GrVkUtil.h"
 
@@ -15,7 +16,7 @@
 #undef CreateSemaphore
 #endif
 
-sk_sp<GrVkSemaphore> GrVkSemaphore::Make(const GrVkGpu* gpu) {
+sk_sp<GrVkSemaphore> GrVkSemaphore::Make(const GrVkGpu* gpu, bool isOwned) {
     VkSemaphoreCreateInfo createInfo;
     memset(&createInfo, 0, sizeof(VkFenceCreateInfo));
     createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
@@ -25,11 +26,22 @@
     GR_VK_CALL_ERRCHECK(gpu->vkInterface(),
                         CreateSemaphore(gpu->device(), &createInfo, nullptr, &semaphore));
 
-    return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore));
+    return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore, isOwned));
 }
 
-GrVkSemaphore::GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore) : INHERITED(gpu) {
-    fResource = new Resource(semaphore);
+sk_sp<GrVkSemaphore> GrVkSemaphore::MakeWrapped(const GrVkGpu* gpu,
+                                                VkSemaphore semaphore,
+                                                GrWrapOwnership ownership) {
+    if (VK_NULL_HANDLE == semaphore) {
+        return nullptr;
+    }
+    return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore,
+                                                  kBorrow_GrWrapOwnership != ownership));
+}
+
+GrVkSemaphore::GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore, bool isOwned)
+        : INHERITED(gpu) {
+    fResource = new Resource(semaphore, isOwned);
 }
 
 GrVkSemaphore::~GrVkSemaphore() {
@@ -41,7 +53,13 @@
 }
 
 void GrVkSemaphore::Resource::freeGPUData(const GrVkGpu* gpu) const {
-    GR_VK_CALL(gpu->vkInterface(),
-               DestroySemaphore(gpu->device(), fSemaphore, nullptr));
+    if (fIsOwned) {
+        GR_VK_CALL(gpu->vkInterface(),
+                   DestroySemaphore(gpu->device(), fSemaphore, nullptr));
+    }
+}
+
+void GrVkSemaphore::setBackendSemaphore(GrBackendSemaphore* backendSemaphore) const {
+    backendSemaphore->initVulkan(fResource->semaphore());
 }
 
diff --git a/src/gpu/vk/GrVkSemaphore.h b/src/gpu/vk/GrVkSemaphore.h
index 0a3bc17..b99eb94 100644
--- a/src/gpu/vk/GrVkSemaphore.h
+++ b/src/gpu/vk/GrVkSemaphore.h
@@ -13,17 +13,23 @@
 
 #include "vk/GrVkTypes.h"
 
+class GrBackendSemaphore;
 class GrVkGpu;
 
 class GrVkSemaphore : public GrSemaphore {
 public:
-    static sk_sp<GrVkSemaphore> Make(const GrVkGpu* gpu);
+    static sk_sp<GrVkSemaphore> Make(const GrVkGpu* gpu, bool isOwned);
+
+    static sk_sp<GrVkSemaphore> MakeWrapped(const GrVkGpu* gpu,
+                                            VkSemaphore semaphore,
+                                            GrWrapOwnership);
 
     ~GrVkSemaphore() override;
 
     class Resource : public GrVkResource {
     public:
-        Resource(VkSemaphore semaphore) : INHERITED(), fSemaphore(semaphore) {}
+        Resource(VkSemaphore semaphore, bool isOwned)
+                : INHERITED(), fSemaphore(semaphore), fIsOwned(isOwned) {}
 
         ~Resource() override {}
 
@@ -38,6 +44,7 @@
         void freeGPUData(const GrVkGpu* gpu) const override;
 
         VkSemaphore fSemaphore;
+        bool        fIsOwned;
 
         typedef GrVkResource INHERITED;
     };
@@ -45,7 +52,9 @@
     const Resource* getResource() const { return fResource; }
 
 private:
-    GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore);
+    GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore, bool isOwned);
+
+    void setBackendSemaphore(GrBackendSemaphore*) const override;
 
     const Resource* fResource;
 
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index cd2a5f1..fbd9f83 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -184,7 +184,19 @@
 }
 
 void SkSurface::prepareForExternalIO() {
-  asSB(this)->onPrepareForExternalIO();
+    this->flush();
+}
+
+void SkSurface::flush() {
+    asSB(this)->onFlush(0, nullptr);
+}
+
+void SkSurface::flushAndSignalSemaphores(int numSemaphores, GrBackendSemaphore* signalSemaphores) {
+    return asSB(this)->onFlush(numSemaphores, signalSemaphores);
+}
+
+void SkSurface::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
+    asSB(this)->onWait(numSemaphores, waitSemaphores);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index 1b0f9ff..264a86f 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -77,8 +77,17 @@
 
     /**
      * Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA.
+     * Inserts the requested number of semaphores for the gpu to signal when work is complete on the
+     * gpu and inits the array of GrBackendSemaphores with the signaled semaphores.
      */
-    virtual void onPrepareForExternalIO() {}
+    virtual void onFlush(int numSemaphores, GrBackendSemaphore* signalSemaphores) {}
+
+    /**
+     * Caused the current backend 3D API to wait on the passed in semaphores before executing new
+     * commands on the gpu. Any previously submitting commands will not be blocked by these
+     * semaphores.
+     */
+    virtual void onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {}
 
     inline SkCanvas* getCachedCanvas();
     inline sk_sp<SkImage> refCachedImage();
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index d558887..0f4b2cb 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -166,8 +166,12 @@
     fDevice->accessRenderTargetContext()->discard();
 }
 
-void SkSurface_Gpu::onPrepareForExternalIO() {
-    fDevice->flush();
+void SkSurface_Gpu::onFlush(int numSemaphores, GrBackendSemaphore* signalSemaphores) {
+    fDevice->flushAndSignalSemaphores(numSemaphores, signalSemaphores);
+}
+
+void SkSurface_Gpu::onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
+    fDevice->wait(numSemaphores, waitSemaphores);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h
index cc8b87d..e22ae10 100644
--- a/src/image/SkSurface_Gpu.h
+++ b/src/image/SkSurface_Gpu.h
@@ -26,7 +26,8 @@
     sk_sp<SkImage> onNewImageSnapshot() override;
     void onCopyOnWrite(ContentChangeMode) override;
     void onDiscard() override;
-    void onPrepareForExternalIO() override;
+    void onFlush(int numSemaphores, GrBackendSemaphore* signalSemaphores) override;
+    void onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) override;
 
     SkGpuDevice* getDevice() { return fDevice.get(); }
 
diff --git a/tests/OnFlushCallbackTest.cpp b/tests/OnFlushCallbackTest.cpp
index f4dd25f..81cd5da 100644
--- a/tests/OnFlushCallbackTest.cpp
+++ b/tests/OnFlushCallbackTest.cpp
@@ -9,6 +9,7 @@
 
 #if SK_SUPPORT_GPU
 
+#include "GrBackendSemaphore.h"
 #include "GrClip.h"
 #include "GrContextPriv.h"
 #include "GrDefaultGeoProcFactory.h"
@@ -575,7 +576,7 @@
         rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
     }
 
-    rtc->prepareForExternalIO();
+    rtc->prepareForExternalIO(0, nullptr);
 
     SkBitmap readBack;
     readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
diff --git a/tests/SurfaceSemaphoreTest.cpp b/tests/SurfaceSemaphoreTest.cpp
new file mode 100644
index 0000000..e9122bb
--- /dev/null
+++ b/tests/SurfaceSemaphoreTest.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+#include "GrTest.h"
+#include "Test.h"
+
+#include "GrBackendSemaphore.h"
+#include "GrBackendSurface.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
+
+#ifdef SK_VULKAN
+#include "vk/GrVkTypes.h"
+#endif
+
+static const int MAIN_W = 8, MAIN_H = 16;
+static const int CHILD_W = 16, CHILD_H = 16;
+
+void check_pixels(skiatest::Reporter* reporter, const SkBitmap& bitmap) {
+    const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
+
+    bool failureFound = false;
+    SkPMColor expectedPixel;
+    for (int cy = 0; cy < CHILD_H && !failureFound; ++cy) {
+        for (int cx = 0; cx < CHILD_W && !failureFound; ++cx) {
+            SkPMColor canvasPixel = canvasPixels[cy * CHILD_W + cx];
+            if (cy < CHILD_H / 2) {
+                if (cx < CHILD_W / 2) {
+                    expectedPixel = 0xFF0000FF; // Red
+                } else {
+                    expectedPixel = 0xFFFF0000; // Blue
+                }
+            } 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);
+            }
+        }
+    }
+}
+
+void draw_child(skiatest::Reporter* reporter,
+                const sk_gpu_test::ContextInfo& childInfo,
+                const GrBackendObject& backendImage,
+                const GrBackendSemaphore& semaphore) {
+    GrBackendTexture backendTexture = GrTest::CreateBackendTexture(childInfo.backend(),
+                                                                   MAIN_W, MAIN_H,
+                                                                   kRGBA_8888_GrPixelConfig,
+                                                                   backendImage);
+
+    childInfo.testContext()->makeCurrent();
+
+    const SkImageInfo childII = SkImageInfo::Make(CHILD_W, CHILD_H, kRGBA_8888_SkColorType,
+                                                  kPremul_SkAlphaType);
+
+    GrContext* childCtx = childInfo.grContext();
+    sk_sp<SkSurface> childSurface(SkSurface::MakeRenderTarget(childCtx, SkBudgeted::kNo,
+                                                              childII, 0, kTopLeft_GrSurfaceOrigin,
+                                                              nullptr));
+
+    sk_sp<SkImage> childImage = SkImage::MakeFromTexture(childCtx,
+                                                         backendTexture,
+                                                         kTopLeft_GrSurfaceOrigin,
+                                                         kPremul_SkAlphaType,
+                                                         nullptr);
+
+    SkCanvas* childCanvas = childSurface->getCanvas();
+    childCanvas->clear(SK_ColorRED);
+
+    childSurface->wait(1, &semaphore);
+
+    childCanvas->drawImage(childImage, CHILD_W/2, 0);
+
+    SkPaint paint;
+    paint.setColor(SK_ColorGREEN);
+    SkIRect rect = SkIRect::MakeLTRB(0, CHILD_H/2, CHILD_W, CHILD_H);
+    childCanvas->drawIRect(rect, paint);
+
+    // read pixels
+    SkBitmap bitmap;
+    bitmap.allocPixels(childII);
+    childCanvas->readPixels(bitmap, 0, 0);
+
+    check_pixels(reporter, bitmap);
+}
+
+void surface_semaphore_test(skiatest::Reporter* reporter,
+                            const sk_gpu_test::ContextInfo& mainInfo,
+                            const sk_gpu_test::ContextInfo& childInfo1,
+                            const sk_gpu_test::ContextInfo& childInfo2) {
+
+    const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType,
+                                             kPremul_SkAlphaType);
+
+    GrContext* mainCtx = mainInfo.grContext();
+    sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(mainCtx, SkBudgeted::kNo,
+                                                             ii, 0, kTopLeft_GrSurfaceOrigin,
+                                                             nullptr));
+    SkCanvas* mainCanvas = mainSurface->getCanvas();
+    mainCanvas->clear(SK_ColorBLUE);
+
+    SkAutoTArray<GrBackendSemaphore> semaphores(2);
+
+    mainSurface->flushAndSignalSemaphores(2, semaphores.get());
+
+    sk_sp<SkImage> mainImage = mainSurface->makeImageSnapshot();
+    GrBackendObject backendImage = mainImage->getTextureHandle(false);
+
+    draw_child(reporter, childInfo1, backendImage, semaphores[0]);
+
+#ifdef SK_VULKAN
+    if (kVulkan_GrBackend == mainInfo.backend()) {
+        // In Vulkan we need to make sure we are sending the correct VkImageLayout in with the
+        // backendImage. After the first child draw the layout gets changed to SHADER_READ, so
+        // we just manually set that here.
+        GrVkImageInfo* vkInfo = (GrVkImageInfo*)backendImage;
+        vkInfo->updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+    }
+#endif
+
+    draw_child(reporter, childInfo2, backendImage, semaphores[1]);
+}
+
+DEF_GPUTEST(SurfaceSemaphores, reporter, factory) {
+#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC)
+    static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGL_ContextType;
+#else
+    static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGLES_ContextType;
+#endif
+
+    for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) {
+        sk_gpu_test::GrContextFactory::ContextType contextType =
+                (sk_gpu_test::GrContextFactory::ContextType) typeInt;
+        // Use "native" instead of explicitly trying OpenGL and OpenGL ES. Do not use GLES on
+        // desktop since tests do not account for not fixing http://skbug.com/2809
+        if (contextType == sk_gpu_test::GrContextFactory::kGL_ContextType ||
+            contextType == sk_gpu_test::GrContextFactory::kGLES_ContextType) {
+            if (contextType != kNativeGLType) {
+                continue;
+            }
+        }
+        sk_gpu_test::ContextInfo ctxInfo = factory->getContextInfo(
+                contextType, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR);
+        if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) {
+            continue;
+        }
+        skiatest::ReporterContext ctx(
+                reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
+        if (ctxInfo.grContext()) {
+            sk_gpu_test::ContextInfo child1 = factory->getSharedContextInfo(ctxInfo.grContext(), 0);
+            sk_gpu_test::ContextInfo child2 = factory->getSharedContextInfo(ctxInfo.grContext(), 1);
+            if (!child1.grContext() || !child2.grContext()) {
+                continue;
+            }
+
+            surface_semaphore_test(reporter, ctxInfo, child1, child2);
+        }
+    }
+}
+
+#endif
diff --git a/tools/gpu/GrContextFactory.h b/tools/gpu/GrContextFactory.h
index dff8d51..508249b 100644
--- a/tools/gpu/GrContextFactory.h
+++ b/tools/gpu/GrContextFactory.h
@@ -83,6 +83,37 @@
         }
     }
 
+    static const char* ContextTypeName(ContextType contextType) {
+        switch (contextType) {
+            case kGL_ContextType:
+                return "OpenGL";
+            case kGLES_ContextType:
+                return "OpenGLES";
+            case kANGLE_D3D9_ES2_ContextType:
+                return "ANGLE D3D9 ES2";
+            case kANGLE_D3D11_ES2_ContextType:
+                return "ANGLE D3D11 ES2";
+            case kANGLE_D3D11_ES3_ContextType:
+                return "ANGLE D3D11 ES3";
+            case kANGLE_GL_ES2_ContextType:
+                return "ANGLE GL ES2";
+            case kANGLE_GL_ES3_ContextType:
+                return "ANGLE GL ES3";
+            case kCommandBuffer_ContextType:
+                return "Command Buffer";
+            case kMESA_ContextType:
+                return "Mesa";
+            case kNullGL_ContextType:
+                return "Null GL";
+            case kDebugGL_ContextType:
+                return "Debug GL";
+            case kVulkan_ContextType:
+                return "Vulkan";
+        }
+        SkDEBUGFAIL("Unreachable");
+        return "Unknown";
+    }
+
     explicit GrContextFactory(const GrContextOptions& opts);
     GrContextFactory();
 
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index cb2f975..ab06158 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -346,7 +346,9 @@
     bool waitFence(GrFence, uint64_t) override { return true; }
     void deleteFence(GrFence) const override {}
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override { return nullptr; }
+    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override { return nullptr; }
+    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+                                            GrWrapOwnership ownership) override { return nullptr; }
     void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override {}
     void waitSemaphore(sk_sp<GrSemaphore> semaphore) override {}
     sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override { return nullptr; }