Add release proc apis to SkSurface::MakeFromBackend* calls.

Bug: skia:
Change-Id: I0e6cd8895c328a89cd0fa50260fe4e8adbff5990
Reviewed-on: https://skia-review.googlesource.com/c/188634
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 23c3b6a..525cdad 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -167,6 +167,15 @@
         return MakeRaster(SkImageInfo::MakeN32Premul(width, height), surfaceProps);
     }
 
+    /** Caller data passed to RenderTarget/TextureReleaseProc; may be nullptr. */
+    typedef void* ReleaseContext;
+
+    /** User function called when supplied render target may be deleted. */
+    typedef void (*RenderTargetReleaseProc)(ReleaseContext releaseContext);
+
+    /** User function called when supplied texture may be deleted. */
+    typedef void (*TextureReleaseProc)(ReleaseContext releaseContext);
+
     /** Wraps a GPU-backed texture into SkSurface. Caller must ensure the texture is
         valid for the lifetime of returned SkSurface. If sampleCnt greater than zero,
         creates an intermediate MSAA SkSurface which is used for drawing backendTexture.
@@ -180,27 +189,31 @@
 
         If SK_SUPPORT_GPU is defined as zero, has no effect and returns nullptr.
 
-        @param context         GPU context
-        @param backendTexture  texture residing on GPU
-        @param origin          one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
-        @param sampleCnt       samples per pixel, or 0 to disable full scene anti-aliasing
-        @param colorType       one of:
-                               kUnknown_SkColorType, kAlpha_8_SkColorType, kRGB_565_SkColorType,
-                               kARGB_4444_SkColorType, kRGBA_8888_SkColorType,
-                               kRGB_888x_SkColorType, kBGRA_8888_SkColorType,
-                               kRGBA_1010102_SkColorType, kRGB_101010x_SkColorType,
-                               kGray_8_SkColorType, kRGBA_F16_SkColorType
-        @param colorSpace      range of colors; may be nullptr
-        @param surfaceProps    LCD striping orientation and setting for device independent
-                               fonts; may be nullptr
-        @return                SkSurface if all parameters are valid; otherwise, nullptr
+        @param context             GPU context
+        @param backendTexture      texture residing on GPU
+        @param origin              one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
+        @param sampleCnt           samples per pixel, or 0 to disable full scene anti-aliasing
+        @param colorType           one of:
+                                   kUnknown_SkColorType, kAlpha_8_SkColorType, kRGB_565_SkColorType,
+                                   kARGB_4444_SkColorType, kRGBA_8888_SkColorType,
+                                   kRGB_888x_SkColorType, kBGRA_8888_SkColorType,
+                                   kRGBA_1010102_SkColorType, kRGB_101010x_SkColorType,
+                                   kGray_8_SkColorType, kRGBA_F16_SkColorType
+        @param colorSpace          range of colors; may be nullptr
+        @param surfaceProps        LCD striping orientation and setting for device independent
+                                   fonts; may be nullptr
+        @param textureReleaseProc  function called when texture can be released
+        @param releaseContext      state passed to textureReleaseProc
+        @return                    SkSurface if all parameters are valid; otherwise, nullptr
     */
     static sk_sp<SkSurface> MakeFromBackendTexture(GrContext* context,
                                                    const GrBackendTexture& backendTexture,
                                                    GrSurfaceOrigin origin, int sampleCnt,
                                                    SkColorType colorType,
                                                    sk_sp<SkColorSpace> colorSpace,
-                                                   const SkSurfaceProps* surfaceProps);
+                                                   const SkSurfaceProps* surfaceProps,
+                                                   TextureReleaseProc textureReleaseProc = nullptr,
+                                                   ReleaseContext releaseContext = nullptr);
 
     /** Wraps a GPU-backed buffer into SkSurface. Caller must ensure backendRenderTarget
         is valid for the lifetime of returned SkSurface.
@@ -214,27 +227,32 @@
 
         If SK_SUPPORT_GPU is defined as zero, has no effect and returns nullptr.
 
-        @param context              GPU context
-        @param backendRenderTarget  GPU intermediate memory buffer
-        @param origin               one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
-        @param colorType            one of:
-                                    kUnknown_SkColorType, kAlpha_8_SkColorType,
-                                    kRGB_565_SkColorType,
-                                    kARGB_4444_SkColorType, kRGBA_8888_SkColorType,
-                                    kRGB_888x_SkColorType, kBGRA_8888_SkColorType,
-                                    kRGBA_1010102_SkColorType, kRGB_101010x_SkColorType,
-                                    kGray_8_SkColorType, kRGBA_F16_SkColorType
-        @param colorSpace           range of colors
-        @param surfaceProps         LCD striping orientation and setting for device independent
-                                    fonts; may be nullptr
-        @return                     SkSurface if all parameters are valid; otherwise, nullptr
+        @param context                  GPU context
+        @param backendRenderTarget      GPU intermediate memory buffer
+        @param origin                   one of:
+                                        kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
+        @param colorType                one of:
+                                        kUnknown_SkColorType, kAlpha_8_SkColorType,
+                                        kRGB_565_SkColorType,
+                                        kARGB_4444_SkColorType, kRGBA_8888_SkColorType,
+                                        kRGB_888x_SkColorType, kBGRA_8888_SkColorType,
+                                        kRGBA_1010102_SkColorType, kRGB_101010x_SkColorType,
+                                        kGray_8_SkColorType, kRGBA_F16_SkColorType
+        @param colorSpace               range of colors
+        @param surfaceProps             LCD striping orientation and setting for device independent
+                                        fonts; may be nullptr
+        @param renderTargetReleaseProc  function called when texture can be released
+        @param releaseContext           state passed to textureReleaseProc
+        @return                         SkSurface if all parameters are valid; otherwise, nullptr
     */
     static sk_sp<SkSurface> MakeFromBackendRenderTarget(GrContext* context,
                                                 const GrBackendRenderTarget& backendRenderTarget,
                                                 GrSurfaceOrigin origin,
                                                 SkColorType colorType,
                                                 sk_sp<SkColorSpace> colorSpace,
-                                                const SkSurfaceProps* surfaceProps);
+                                                const SkSurfaceProps* surfaceProps,
+                                                TextureReleaseProc textureReleaseProc = nullptr,
+                                                ReleaseContext releaseContext = nullptr);
 
     /** Wraps a GPU-backed texture into SkSurface. Caller must ensure backendTexture is
         valid for the lifetime of returned SkSurface. If sampleCnt greater than zero,
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index e1588ae..7701011 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -886,12 +886,15 @@
                                                                    GrSurfaceOrigin origin,
                                                                    int sampleCnt,
                                                                    sk_sp<SkColorSpace> colorSpace,
-                                                                   const SkSurfaceProps* props) {
+                                                                   const SkSurfaceProps* props,
+                                                                   ReleaseProc releaseProc,
+                                                                   ReleaseContext releaseCtx) {
     ASSERT_SINGLE_OWNER_PRIV
     SkASSERT(sampleCnt > 0);
 
     sk_sp<GrTextureProxy> proxy(this->proxyProvider()->wrapRenderableBackendTexture(
-            tex, origin, sampleCnt, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo));
+            tex, origin, sampleCnt, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, releaseProc,
+            releaseCtx));
     if (!proxy) {
         return nullptr;
     }
@@ -904,10 +907,13 @@
                                                 const GrBackendRenderTarget& backendRT,
                                                 GrSurfaceOrigin origin,
                                                 sk_sp<SkColorSpace> colorSpace,
-                                                const SkSurfaceProps* surfaceProps) {
+                                                const SkSurfaceProps* surfaceProps,
+                                                ReleaseProc releaseProc,
+                                                ReleaseContext releaseCtx) {
     ASSERT_SINGLE_OWNER_PRIV
 
-    sk_sp<GrSurfaceProxy> proxy = this->proxyProvider()->wrapBackendRenderTarget(backendRT, origin);
+    sk_sp<GrSurfaceProxy> proxy = this->proxyProvider()->wrapBackendRenderTarget(
+            backendRT, origin, releaseProc, releaseCtx);
     if (!proxy) {
         return nullptr;
     }
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 02dc7a3..5b36351 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -70,18 +70,26 @@
                                                       GrSurfaceOrigin origin,
                                                       sk_sp<SkColorSpace> colorSpace);
 
+    // These match the definitions in SkSurface & GrSurface.h, for whence they came
+    typedef void* ReleaseContext;
+    typedef void (*ReleaseProc)(ReleaseContext);
+
     sk_sp<GrRenderTargetContext> makeBackendTextureRenderTargetContext(
                                                          const GrBackendTexture& tex,
                                                          GrSurfaceOrigin origin,
                                                          int sampleCnt,
                                                          sk_sp<SkColorSpace> colorSpace,
-                                                         const SkSurfaceProps* = nullptr);
+                                                         const SkSurfaceProps* = nullptr,
+                                                         ReleaseProc = nullptr,
+                                                         ReleaseContext = nullptr);
 
     sk_sp<GrRenderTargetContext> makeBackendRenderTargetRenderTargetContext(
                                                               const GrBackendRenderTarget&,
                                                               GrSurfaceOrigin origin,
                                                               sk_sp<SkColorSpace> colorSpace,
-                                                              const SkSurfaceProps* = nullptr);
+                                                              const SkSurfaceProps* = nullptr,
+                                                              ReleaseProc = nullptr,
+                                                              ReleaseContext = nullptr);
 
     sk_sp<GrRenderTargetContext> makeBackendTextureAsRenderTargetRenderTargetContext(
                                                                  const GrBackendTexture& tex,
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index f13cf7c..46f2709 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -475,7 +475,7 @@
     if (releaseProc) {
         releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx));
         // This gives the texture a ref on the releaseHelper
-        tex->setRelease(releaseHelper);
+        tex->setRelease(std::move(releaseHelper));
     }
 
     SkASSERT(!tex->asRenderTarget());  // Strictly a GrTexture
@@ -487,7 +487,8 @@
 
 sk_sp<GrTextureProxy> GrProxyProvider::wrapRenderableBackendTexture(
         const GrBackendTexture& backendTex, GrSurfaceOrigin origin, int sampleCnt,
-        GrWrapOwnership ownership, GrWrapCacheable cacheable) {
+        GrWrapOwnership ownership, GrWrapCacheable cacheable, ReleaseProc releaseProc,
+        ReleaseContext releaseCtx) {
     if (this->isAbandoned()) {
         return nullptr;
     }
@@ -508,6 +509,13 @@
         return nullptr;
     }
 
+    sk_sp<GrReleaseProcHelper> releaseHelper;
+    if (releaseProc) {
+        releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx));
+        // This gives the texture a ref on the releaseHelper
+        tex->setRelease(std::move(releaseHelper));
+    }
+
     SkASSERT(tex->asRenderTarget());  // A GrTextureRenderTarget
     // Make sure we match how we created the proxy with SkBudgeted::kNo
     SkASSERT(GrBudgetedType::kBudgeted != tex->resourcePriv().budgetedType());
@@ -516,7 +524,8 @@
 }
 
 sk_sp<GrSurfaceProxy> GrProxyProvider::wrapBackendRenderTarget(
-        const GrBackendRenderTarget& backendRT, GrSurfaceOrigin origin) {
+        const GrBackendRenderTarget& backendRT, GrSurfaceOrigin origin, ReleaseProc releaseProc,
+        ReleaseContext releaseCtx) {
     if (this->isAbandoned()) {
         return nullptr;
     }
@@ -530,6 +539,14 @@
     if (!rt) {
         return nullptr;
     }
+
+    sk_sp<GrReleaseProcHelper> releaseHelper;
+    if (releaseProc) {
+        releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx));
+        // This gives the render target a ref on the releaseHelper
+        rt->setRelease(std::move(releaseHelper));
+    }
+
     SkASSERT(!rt->asTexture());  // A GrRenderTarget that's not textureable
     SkASSERT(!rt->getUniqueKey().isValid());
     // Make sure we match how we created the proxy with SkBudgeted::kNo
diff --git a/src/gpu/GrProxyProvider.h b/src/gpu/GrProxyProvider.h
index f093416..f7481e5 100644
--- a/src/gpu/GrProxyProvider.h
+++ b/src/gpu/GrProxyProvider.h
@@ -121,12 +121,14 @@
      */
     sk_sp<GrTextureProxy> wrapRenderableBackendTexture(const GrBackendTexture&, GrSurfaceOrigin,
                                                        int sampleCnt, GrWrapOwnership,
-                                                       GrWrapCacheable);
+                                                       GrWrapCacheable, ReleaseProc,
+                                                       ReleaseContext);
 
     /*
      * Create a render target proxy that wraps a backend render target
      */
-    sk_sp<GrSurfaceProxy> wrapBackendRenderTarget(const GrBackendRenderTarget&, GrSurfaceOrigin);
+    sk_sp<GrSurfaceProxy> wrapBackendRenderTarget(const GrBackendRenderTarget&, GrSurfaceOrigin,
+                                                  ReleaseProc, ReleaseContext);
 
     /*
      * Create a render target proxy that wraps a backend texture
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index c51ce9b..272e18b 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -308,7 +308,8 @@
 sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext*, const GrBackendTexture&,
                                                    GrSurfaceOrigin origin, int sampleCnt,
                                                    SkColorType, sk_sp<SkColorSpace>,
-                                                   const SkSurfaceProps*) {
+                                                   const SkSurfaceProps*,
+                                                   TextureReleaseProc, ReleaseContext) {
     return nullptr;
 }
 
@@ -317,7 +318,8 @@
                                                         GrSurfaceOrigin origin,
                                                         SkColorType,
                                                         sk_sp<SkColorSpace>,
-                                                        const SkSurfaceProps*) {
+                                                        const SkSurfaceProps*,
+                                                        RenderTargetReleaseProc, ReleaseContext) {
     return nullptr;
 }
 
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 89959f1..7163f21 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -421,7 +421,9 @@
                                                    GrSurfaceOrigin origin, int sampleCnt,
                                                    SkColorType colorType,
                                                    sk_sp<SkColorSpace> colorSpace,
-                                                   const SkSurfaceProps* props) {
+                                                   const SkSurfaceProps* props,
+                                                   SkSurface::TextureReleaseProc textureReleaseProc,
+                                                   SkSurface::ReleaseContext releaseContext) {
     if (!context) {
         return nullptr;
     }
@@ -445,7 +447,9 @@
         origin,
         sampleCnt,
         std::move(colorSpace),
-        props));
+        props,
+        textureReleaseProc,
+        releaseContext));
     if (!rtc) {
         return nullptr;
     }
@@ -490,7 +494,9 @@
                                                         GrSurfaceOrigin origin,
                                                         SkColorType colorType,
                                                         sk_sp<SkColorSpace> colorSpace,
-                                                        const SkSurfaceProps* props) {
+                                                        const SkSurfaceProps* props,
+                                                        SkSurface::RenderTargetReleaseProc relProc,
+                                                        SkSurface::ReleaseContext releaseContext) {
     if (!context) {
         return nullptr;
     }
@@ -509,7 +515,7 @@
 
     sk_sp<GrRenderTargetContext> rtc(
             context->priv().makeBackendRenderTargetRenderTargetContext(
-                    rtCopy, origin, std::move(colorSpace), props));
+                    rtCopy, origin, std::move(colorSpace), props, relProc, releaseContext));
     if (!rtc) {
         return nullptr;
     }
diff --git a/tests/ProxyConversionTest.cpp b/tests/ProxyConversionTest.cpp
index 638092d..58ba26b 100644
--- a/tests/ProxyConversionTest.cpp
+++ b/tests/ProxyConversionTest.cpp
@@ -29,7 +29,7 @@
     GrSRGBEncoded srgbEncoded;
     auto ct = GrPixelConfigToColorTypeAndEncoding(desc.fConfig, &srgbEncoded);
     auto backendRT = gpu->createTestingOnlyBackendRenderTarget(desc.fWidth, desc.fHeight, ct);
-    return provider->wrapBackendRenderTarget(backendRT, origin);
+    return provider->wrapBackendRenderTarget(backendRT, origin, nullptr, nullptr);
 }
 
 void clean_up_wrapped_rt(GrGpu* gpu, sk_sp<GrSurfaceProxy> proxy) {
diff --git a/tests/ProxyTest.cpp b/tests/ProxyTest.cpp
index b638069..87b92ee 100644
--- a/tests/ProxyTest.cpp
+++ b/tests/ProxyTest.cpp
@@ -228,7 +228,8 @@
                 GrBackendRenderTarget backendRT = gpu->createTestingOnlyBackendRenderTarget(
                         kWidthHeight, kWidthHeight, SkColorTypeToGrColorType(colorType));
                 sk_sp<GrSurfaceProxy> sProxy(
-                        proxyProvider->wrapBackendRenderTarget(backendRT, origin));
+                        proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr,
+                                                               nullptr));
                 check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight,
                               backendRT.pixelConfig(), SkBudgeted::kNo);
                 static constexpr int kExpectedNumSamples = 1;
@@ -258,7 +259,8 @@
                                                     kStencilBits, fboInfo);
                     backendRT.setPixelConfig(config);
                     sk_sp<GrSurfaceProxy> sProxy(
-                            proxyProvider->wrapBackendRenderTarget(backendRT, origin));
+                            proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr,
+                                                                   nullptr));
                     check_surface(reporter, sProxy.get(), origin,
                                   kWidthHeight, kWidthHeight,
                                   backendRT.pixelConfig(), SkBudgeted::kNo);
@@ -300,7 +302,7 @@
 
                     sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapRenderableBackendTexture(
                             backendTex, origin, supportedNumSamples, kBorrow_GrWrapOwnership,
-                            GrWrapCacheable::kNo);
+                            GrWrapCacheable::kNo, nullptr, nullptr);
                     if (!sProxy) {
                         gpu->deleteTestingOnlyBackendTexture(backendTex);
                         continue;  // This can fail on Mesa
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 166ac6b..78e068a 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -869,6 +869,76 @@
     }
 }
 
+struct ReleaseChecker {
+    ReleaseChecker() : fReleaseCount(0) {}
+    int fReleaseCount;
+    static void Release(void* self) {
+        static_cast<ReleaseChecker*>(self)->fReleaseCount++;
+    }
+};
+
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceWrappedWithRelease_Gpu, reporter, ctxInfo) {
+    const int kWidth = 10;
+    const int kHeight = 10;
+    std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
+
+    GrContext* ctx = ctxInfo.grContext();
+    GrGpu* gpu = ctx->priv().getGpu();
+
+    for (bool useTexture : {false, true}) {
+        GrBackendTexture backendTex;
+        GrBackendRenderTarget backendRT;
+        sk_sp<SkSurface> surface;
+
+        ReleaseChecker releaseChecker;
+        GrSurfaceOrigin texOrigin = kBottomLeft_GrSurfaceOrigin;
+
+        if (useTexture) {
+            backendTex = gpu->createTestingOnlyBackendTexture(
+                pixels.get(), kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
+            if (!backendTex.isValid()) {
+                continue;
+            }
+
+            surface = SkSurface::MakeFromBackendTexture(ctx, backendTex, texOrigin, 1,
+                                                        kRGBA_8888_SkColorType,
+                                                        nullptr, nullptr,
+                                                        ReleaseChecker::Release,
+                                                        &releaseChecker);
+        } else {
+            backendRT = gpu->createTestingOnlyBackendRenderTarget(kWidth, kHeight,
+                                                                  GrColorType::kRGBA_8888);
+            if (!backendRT.isValid()) {
+                continue;
+            }
+            surface = SkSurface::MakeFromBackendRenderTarget(ctx, backendRT, texOrigin,
+                                                             kRGBA_8888_SkColorType,
+                                                             nullptr, nullptr,
+                                                             ReleaseChecker::Release,
+                                                             &releaseChecker);
+        }
+        if (!surface) {
+            ERRORF(reporter, "Failed to create surface");
+            continue;
+        }
+
+        surface->getCanvas()->clear(SK_ColorRED);
+        surface->flush();
+        gpu->testingOnly_flushGpuAndSync();
+
+        // Now exercise the release proc
+        REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+        surface.reset(nullptr); // force a release of the surface
+        REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+
+        if (useTexture) {
+            gpu->deleteTestingOnlyBackendTexture(backendTex);
+        } else {
+            gpu->deleteTestingOnlyBackendRenderTarget(backendRT);
+        }
+    }
+}
 
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SurfaceAttachStencil_Gpu, reporter, ctxInfo) {
     GrGpu* gpu = ctxInfo.grContext()->priv().getGpu();
diff --git a/tools/gpu/ProxyUtils.cpp b/tools/gpu/ProxyUtils.cpp
index d9f00e6..31b9ddd 100644
--- a/tools/gpu/ProxyUtils.cpp
+++ b/tools/gpu/ProxyUtils.cpp
@@ -33,7 +33,8 @@
         // Adopt ownership so our caller doesn't have to worry about deleting the backend texture.
         if (isRT) {
             proxy = context->priv().proxyProvider()->wrapRenderableBackendTexture(
-                    backendTex, origin, 1, kAdopt_GrWrapOwnership, GrWrapCacheable::kNo);
+                    backendTex, origin, 1, kAdopt_GrWrapOwnership, GrWrapCacheable::kNo, nullptr,
+                    nullptr);
         } else {
             proxy = context->priv().proxyProvider()->wrapBackendTexture(
                     backendTex, origin, kAdopt_GrWrapOwnership, GrWrapCacheable::kNo, kRW_GrIOType);