canvas2d: Manage lifetime of SharedImage resource for StaticBitmapImage.

The StaticBitmapImage holds a reference to the SharedImage's mailbox
used by a canvas2d instance. Use a callback which wraps the
CanvasResource that owns this SharedImage to both ensure that the
SharedImage stays alive for the lifetime of the StaticBitmapImage and
the resource is recycled once all refs to the StaticBitmapImage have
been released.

R=kbr@chromium.org, piman@chromium.org

Bug: 948133
Change-Id: Ibc6f578fd8656a90b32a0267511d723f20cf745e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1610520
Commit-Queue: Khushal <khushalsagar@chromium.org>
Auto-Submit: Khushal <khushalsagar@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Antoine Labour <piman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#660271}
diff --git a/third_party/blink/renderer/platform/DEPS b/third_party/blink/renderer/platform/DEPS
index 4c6a6c2..c1d52350 100644
--- a/third_party/blink/renderer/platform/DEPS
+++ b/third_party/blink/renderer/platform/DEPS
@@ -54,6 +54,7 @@
     "+net/http/http_response_headers.h",
     "+device",
     "+gpu/GLES2",
+    "+gpu/command_buffer/common/sync_token.h",
     "+mojo/public",
     "+mozilla",
     "+services/metrics/public/cpp/ukm_entry_builder.h",
diff --git a/third_party/blink/renderer/platform/cross_thread_copier.h b/third_party/blink/renderer/platform/cross_thread_copier.h
index 84c033db..00383e1 100644
--- a/third_party/blink/renderer/platform/cross_thread_copier.h
+++ b/third_party/blink/renderer/platform/cross_thread_copier.h
@@ -35,6 +35,7 @@
 #include <vector>
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/common/sync_token.h"
 #include "mojo/public/cpp/bindings/interface_ptr_info.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
@@ -142,6 +143,12 @@
   STATIC_ONLY(CrossThreadCopier);
 };
 
+template <>
+struct CrossThreadCopier<gpu::SyncToken>
+    : public CrossThreadCopierPassThrough<gpu::SyncToken> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
 // nullptr_t can be passed through without any changes.
 template <>
 struct CrossThreadCopier<std::nullptr_t>
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
index 228a93b..4f10d77 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 
+#include "components/viz/common/resources/single_release_callback.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/sync_token.h"
@@ -41,10 +42,11 @@
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper,
     IntSize mailbox_size,
-    MailboxType mailbox_type) {
+    MailboxType mailbox_type,
+    std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
   return base::AdoptRef(new AcceleratedStaticBitmapImage(
       mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
-      mailbox_size, mailbox_type));
+      mailbox_size, mailbox_type, std::move(release_callback)));
 }
 
 AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
@@ -65,9 +67,11 @@
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper,
     IntSize mailbox_size,
-    MailboxType mailbox_type)
+    MailboxType mailbox_type,
+    std::unique_ptr<viz::SingleReleaseCallback> release_callback)
     : paint_image_content_id_(cc::PaintImage::GetNextContentId()),
-      mailbox_type_(mailbox_type) {
+      mailbox_type_(mailbox_type),
+      release_callback_(std::move(release_callback)) {
   texture_holder_ = std::make_unique<MailboxTextureHolder>(
       mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
       mailbox_size);
@@ -100,6 +104,11 @@
 AcceleratedStaticBitmapImage::~AcceleratedStaticBitmapImage() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  if (release_callback_) {
+    release_callback_->Run(texture_holder_->GetSyncToken(),
+                           false /* is_lost */);
+  }
+
   // If the original SkImage was retained, it must be destroyed on the thread
   // where it came from. In the same thread case, there is nothing to do because
   // the regular destruction flow is fine.
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
index f6ff2f2..d9e1a8e 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
@@ -16,6 +16,10 @@
 
 class GrContext;
 
+namespace viz {
+class SingleReleaseCallback;
+}  // namespace viz
+
 namespace blink {
 class WebGraphicsContext3DProviderWrapper;
 class TextureHolder;
@@ -32,10 +36,13 @@
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>);
 
   // Can specify the GrContext that created the texture backing. Ideally all
-  // callers would use this option. The |mailbox| is a name for the texture
-  // backing, allowing other contexts to use the same backing. |mailbox_type|
-  // indicates whether |mailbox| is a SharedImage identifier or a deprecated
-  // mailbox (generated via ProduceTextureDirectCHROMIUM).
+  // callers would use this option.
+  // The |mailbox| is a name for the texture backing, allowing other contexts to
+  // use the same backing.
+  // |mailbox_type| indicates whether |mailbox| is a SharedImage identifier or a
+  // deprecated mailbox (generated via ProduceTextureDirectCHROMIUM).
+  // |release_callback| is an optional callback to be invoked when this image
+  // is destroyed. It can be invoked on any thread.
   static scoped_refptr<AcceleratedStaticBitmapImage>
   CreateFromWebGLContextImage(
       const gpu::Mailbox&,
@@ -43,7 +50,8 @@
       unsigned texture_id,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
       IntSize mailbox_size,
-      MailboxType mailbox_type = MailboxType::kDeprecatedMailbox);
+      MailboxType mailbox_type = MailboxType::kDeprecatedMailbox,
+      std::unique_ptr<viz::SingleReleaseCallback> release_callback = nullptr);
 
   bool CurrentFrameKnownToBeOpaque() override;
   IntSize Size() const override;
@@ -107,7 +115,8 @@
       unsigned texture_id,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
       IntSize mailbox_size,
-      MailboxType mailbox_type);
+      MailboxType mailbox_type,
+      std::unique_ptr<viz::SingleReleaseCallback> release_callback);
 
   void CreateImageFromMailboxIfNeeded();
   void WaitSyncTokenIfNeeded();
@@ -125,6 +134,7 @@
       original_skia_image_context_provider_wrapper_;
 
   const MailboxType mailbox_type_;
+  std::unique_ptr<viz::SingleReleaseCallback> release_callback_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 695207e..1abda19 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -16,12 +16,15 @@
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
+#include "gpu/command_buffer/common/sync_token.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "ui/gfx/buffer_format_util.h"
@@ -760,11 +763,41 @@
   mailbox_needs_new_sync_token_ = true;
 }
 
+// static
+void CanvasResourceSharedImage::OnBitmapImageDestroyed(
+    scoped_refptr<CanvasResourceSharedImage> resource,
+    scoped_refptr<base::SingleThreadTaskRunner> original_task_runner,
+    const gpu::SyncToken& sync_token,
+    bool is_lost) {
+  if (!original_task_runner->BelongsToCurrentThread()) {
+    PostCrossThreadTask(
+        *original_task_runner, FROM_HERE,
+        CrossThreadBind(&CanvasResourceSharedImage::OnBitmapImageDestroyed,
+                        std::move(resource), std::move(original_task_runner),
+                        sync_token, is_lost));
+    return;
+  }
+
+  auto weak_provider = resource->WeakProvider();
+  ReleaseFrameResources(std::move(weak_provider), std::move(resource),
+                        sync_token, is_lost);
+}
+
 scoped_refptr<StaticBitmapImage> CanvasResourceSharedImage::Bitmap() {
+  // The |release_callback| keeps a ref on this resource to ensure the backing
+  // shared image is kept alive until the lifetime of the image.
+  // Note that the code in CanvasResourceProvider::RecycleResource also uses the
+  // ref-count on the resource as a proxy for a read lock to allow recycling the
+  // resource once all refs have been released.
+  auto release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
+      &OnBitmapImageDestroyed, scoped_refptr<CanvasResourceSharedImage>(this),
+      Thread::Current()->GetTaskRunner()));
+
   scoped_refptr<StaticBitmapImage> image =
       AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
           shared_image_mailbox_, GetSyncToken(), 0, ContextProviderWrapper(),
-          Size());
+          Size(), AcceleratedStaticBitmapImage::MailboxType::kSharedImageId,
+          std::move(release_callback));
   DCHECK(image);
   return image;
 }
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index cc98f5c1..de43b18 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -122,6 +122,7 @@
   const CanvasColorParams& ColorParams() const { return color_params_; }
   void OnDestroy();
   CanvasResourceProvider* Provider() { return provider_.get(); }
+  base::WeakPtr<CanvasResourceProvider> WeakProvider() { return provider_; }
 
  private:
   // Sync token that was provided when resource was released
@@ -340,6 +341,12 @@
   void WillDraw();
 
  private:
+  static void OnBitmapImageDestroyed(
+      scoped_refptr<CanvasResourceSharedImage> resource,
+      scoped_refptr<base::SingleThreadTaskRunner> original_task_runner,
+      const gpu::SyncToken& sync_token,
+      bool is_lost);
+
   void TearDown() override;
   base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
       const override;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index 4b70403..e94a608 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -269,6 +269,41 @@
   EXPECT_NE(sync_token, resource_again->GetSyncToken());
 }
 
+TEST_F(CanvasResourceProviderTest,
+       CanvasResourceProviderSharedImageStaticBitmapImage) {
+  const IntSize kSize(10, 10);
+  const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
+                                       kRGBA8CanvasPixelFormat, kNonOpaque);
+  EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
+
+  auto provider = CanvasResourceProvider::Create(
+      kSize, CanvasResourceProvider::kCreateSharedImageForTesting,
+      context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
+      CanvasResourceProvider::kAllowImageChromiumPresentationMode,
+      nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+  ASSERT_TRUE(provider->IsValid());
+
+  // Same resource returned until the canvas is updated.
+  auto image = provider->Snapshot();
+  ASSERT_TRUE(image);
+  auto new_image = provider->Snapshot();
+  EXPECT_EQ(image->GetMailbox(), new_image->GetMailbox());
+  EXPECT_EQ(provider->ProduceCanvasResource()->GetOrCreateGpuMailbox(
+                kOrderingBarrier),
+            image->GetMailbox());
+
+  // Resource updated after draw.
+  provider->Canvas()->clear(SK_ColorWHITE);
+  new_image = provider->Snapshot();
+  EXPECT_NE(new_image->GetMailbox(), image->GetMailbox());
+
+  // Resource recycled.
+  auto original_mailbox = image->GetMailbox();
+  image.reset();
+  provider->Canvas()->clear(SK_ColorBLACK);
+  EXPECT_EQ(original_mailbox, provider->Snapshot()->GetMailbox());
+}
+
 TEST_F(CanvasResourceProviderTest, CanvasResourceProviderBitmap) {
   const IntSize kSize(10, 10);
   const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,