Moves cc::Surfaces for Videos onto its own thread.

Previously we were running the cc::Surface for Videos project on the media thread.
This was found to cause some smoothness regressions. This CL fixes that by pushing the
work onto its own thread.

TBR=gab@chromium.org

Bug: 866508
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ide12cb6cebc9dea83a7686c09e6688537bf3389a
Reviewed-on: https://chromium-review.googlesource.com/1176879
Commit-Queue: CJ DiMeglio <lethalantidote@chromium.org>
Reviewed-by: François Doray <fdoray@chromium.org>
Reviewed-by: Mounir Lamouri <mlamouri@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#587811}
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 791f4eb..64a5e8f 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -23,6 +23,9 @@
 namespace audio {
 class OutputDevice;
 }
+namespace blink {
+class VideoFrameResourceProvider;
+}
 namespace cc {
 class CompletionEvent;
 class SingleThreadTaskGraphRunner;
@@ -330,6 +333,7 @@
       LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
   friend class webrtc::DesktopConfigurationMonitor;
   friend class content::ServiceWorkerSubresourceLoader;
+  friend class blink::VideoFrameResourceProvider;
 
   ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
   ~ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index f94c128..ce032999 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -24,6 +24,7 @@
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/render_view_impl.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/cdm_factory.h"
 #include "media/base/decoder_factory.h"
 #include "media/base/media_switches.h"
@@ -100,38 +101,26 @@
   DISALLOW_COPY_AND_ASSIGN(FrameFetchContext);
 };
 
-void ObtainAndSetContextProvider(
-    base::OnceCallback<void(bool,
-                            scoped_refptr<ws::ContextProviderCommandBuffer>)>
-        set_context_provider_callback,
-    std::pair<media::GpuVideoAcceleratorFactories*, bool> gpu_info) {
-  if (gpu_info.first) {
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider =
-        gpu_info.first->GetMediaContextProvider();
-    std::move(set_context_provider_callback)
-        .Run(gpu_info.second, std::move(context_provider));
-  } else {
-    std::move(set_context_provider_callback).Run(false, nullptr);
-  }
-}
-
 // Obtains the media ContextProvider and calls the given callback on the same
 // thread this is called on. Obtaining the media ContextProvider requires
-// getting GPuVideoAcceleratorFactories, which must be done on the main
-// thread.
-void PostMediaContextProviderToCallback(
+// establishing a GPUChannelHost, which must be done on the main thread.
+void PostContextProviderToCallback(
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
-    base::OnceCallback<void(bool,
-                            scoped_refptr<ws::ContextProviderCommandBuffer>)>
-        set_context_provider_callback) {
-  base::PostTaskAndReplyWithResult(
-      main_task_runner.get(), FROM_HERE, base::BindOnce([]() {
-        return std::pair<media::GpuVideoAcceleratorFactories*, bool>(
-            content::RenderThreadImpl::current()->GetGpuFactories(),
-            !content::RenderThreadImpl::current()->IsGpuCompositingDisabled());
-      }),
-      base::BindOnce(&ObtainAndSetContextProvider,
-                     std::move(set_context_provider_callback)));
+    scoped_refptr<viz::ContextProvider> unwanted_context_provider,
+    blink::WebSubmitterConfigurationCallback set_context_provider_callback) {
+  main_task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](scoped_refptr<viz::ContextProvider> unwanted_context_provider,
+             blink::WebSubmitterConfigurationCallback cb) {
+            auto* rti = content::RenderThreadImpl::current();
+            auto context_provider = rti->GetVideoFrameCompositorContextProvider(
+                unwanted_context_provider);
+            std::move(cb).Run(!rti->IsGpuCompositingDisabled(),
+                              std::move(context_provider));
+          },
+          std::move(unwanted_context_provider),
+          media::BindToCurrentLoop(std::move(set_context_provider_callback))));
 }
 
 }  // namespace
@@ -307,12 +296,11 @@
   std::unique_ptr<blink::WebVideoFrameSubmitter> submitter;
   bool use_surface_layer_for_video = VideoSurfaceLayerEnabled();
   if (use_surface_layer_for_video) {
-    // TODO(lethalantidote): Use a separate task_runner. https://crbug/753605.
     video_frame_compositor_task_runner =
-        render_thread->GetMediaThreadTaskRunner();
+        render_thread->CreateVideoFrameCompositorTaskRunner();
     submitter = blink::WebVideoFrameSubmitter::Create(
         base::BindRepeating(
-            &PostMediaContextProviderToCallback,
+            &PostContextProviderToCallback,
             RenderThreadImpl::current()->GetCompositorMainThreadTaskRunner()),
         settings);
   } else {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 86833909..e0208d4 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -31,6 +31,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
 #include "base/threading/simple_thread.h"
 #include "base/threading/thread_local.h"
 #include "base/threading/thread_restrictions.h"
@@ -1191,6 +1192,21 @@
 #endif
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+RenderThreadImpl::CreateVideoFrameCompositorTaskRunner() {
+  if (!video_frame_compositor_task_runner_) {
+    // All of Chromium's GPU code must know which thread it's running on, and
+    // be the same thread on which the rendering context was initialized. This
+    // is why this must be a SingleThreadTaskRunner instead of a
+    // SequencedTaskRunner.
+    video_frame_compositor_task_runner_ =
+        base::CreateSingleThreadTaskRunnerWithTraits(
+            {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+  }
+
+  return video_frame_compositor_task_runner_;
+}
+
 void RenderThreadImpl::InitializeWebKit(
     service_manager::BinderRegistry* registry) {
   DCHECK(!blink_platform_impl_);
@@ -1434,6 +1450,39 @@
   return gpu_factories_.back().get();
 }
 
+scoped_refptr<viz::ContextProvider>
+RenderThreadImpl::GetVideoFrameCompositorContextProvider(
+    scoped_refptr<viz::ContextProvider> unwanted_context_provider) {
+  if (video_frame_compositor_context_provider_ &&
+      video_frame_compositor_context_provider_ != unwanted_context_provider) {
+    return video_frame_compositor_context_provider_;
+  }
+
+  video_frame_compositor_context_provider_ = nullptr;
+
+  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
+      EstablishGpuChannelSync();
+  if (!gpu_channel_host)
+    return nullptr;
+
+  // This context is only used to create textures and mailbox them, so
+  // use lower limits than the default.
+  gpu::SharedMemoryLimits limits = gpu::SharedMemoryLimits::ForMailboxContext();
+
+  bool support_locking = false;
+  bool support_gles2_interface = true;
+  bool support_raster_interface = false;
+  bool support_oop_rasterization = false;
+  bool support_grcontext = false;
+  video_frame_compositor_context_provider_ = CreateOffscreenContext(
+      gpu_channel_host, GetGpuMemoryBufferManager(), limits, support_locking,
+      support_gles2_interface, support_raster_interface,
+      support_oop_rasterization, support_grcontext,
+      ws::command_buffer_metrics::ContextType::RENDER_COMPOSITOR,
+      kGpuStreamIdMedia, kGpuStreamPriorityMedia);
+  return video_frame_compositor_context_provider_;
+}
+
 scoped_refptr<ws::ContextProviderCommandBuffer>
 RenderThreadImpl::SharedMainThreadContextProvider() {
   DCHECK(IsMainThread());
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index b14c1b9..61d7956 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -392,6 +392,13 @@
   // A TaskRunner instance that runs tasks on the raster worker pool.
   base::TaskRunner* GetWorkerTaskRunner();
 
+  // Creates a ContextProvider if yet created, and returns it to be used for
+  // video frame compositing. The ContextProvider given as an argument is
+  // one that has been lost, and is a hint to the RenderThreadImpl to clear
+  // it's |video_frame_compositor_context_provider_| if it matches.
+  scoped_refptr<viz::ContextProvider> GetVideoFrameCompositorContextProvider(
+      scoped_refptr<viz::ContextProvider>);
+
   // Returns a worker context provider that will be bound on the compositor
   // thread.
   scoped_refptr<viz::RasterContextProvider>
@@ -508,6 +515,9 @@
   // Sets the current pipeline rendering color space.
   void SetRenderingColorSpace(const gfx::ColorSpace& color_space);
 
+  scoped_refptr<base::SingleThreadTaskRunner>
+  CreateVideoFrameCompositorTaskRunner();
+
  private:
   void OnProcessFinalRelease() override;
   // IPC::Listener
@@ -676,6 +686,10 @@
   // regardless of whether |compositor_thread_| is overriden.
   scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
 
+  // Task to run the VideoFrameCompositor on.
+  scoped_refptr<base::SingleThreadTaskRunner>
+      video_frame_compositor_task_runner_;
+
   // Pool of workers used for raster operations (e.g., tile rasterization).
   scoped_refptr<CategorizedWorkerPool> categorized_worker_pool_;
 
@@ -687,6 +701,8 @@
 
   base::ObserverList<RenderThreadObserver>::Unchecked observers_;
 
+  scoped_refptr<viz::ContextProvider> video_frame_compositor_context_provider_;
+
   scoped_refptr<viz::RasterContextProvider> shared_worker_context_provider_;
 
   std::unique_ptr<AudioRendererMixerManager> audio_renderer_mixer_manager_;
diff --git a/third_party/blink/public/platform/web_video_frame_submitter.h b/third_party/blink/public/platform/web_video_frame_submitter.h
index e4110cd..a6a61ed 100644
--- a/third_party/blink/public/platform/web_video_frame_submitter.h
+++ b/third_party/blink/public/platform/web_video_frame_submitter.h
@@ -14,17 +14,20 @@
 class LayerTreeSettings;
 }
 
-namespace ws {
-class ContextProviderCommandBuffer;
-}  // namespace ui
+namespace viz {
+class ContextProvider;
+}  // namespace viz
 
 namespace blink {
 
+// Sets the proper context_provider and compositing mode onto the Submitter.
+using WebSubmitterConfigurationCallback =
+    base::OnceCallback<void(bool, scoped_refptr<viz::ContextProvider>)>;
 // Callback to obtain the media ContextProvider and a bool indicating whether
 // we are in software compositing mode.
-using WebContextProviderCallback = base::RepeatingCallback<void(
-    base::OnceCallback<void(bool,
-                            scoped_refptr<ws::ContextProviderCommandBuffer>)>)>;
+using WebContextProviderCallback =
+    base::RepeatingCallback<void(scoped_refptr<viz::ContextProvider>,
+                                 WebSubmitterConfigurationCallback)>;
 using WebFrameSinkDestroyedCallback = base::RepeatingCallback<void()>;
 
 // Exposes the VideoFrameSubmitter, which submits CompositorFrames containing
diff --git a/third_party/blink/renderer/platform/graphics/DEPS b/third_party/blink/renderer/platform/graphics/DEPS
index f8c18a1..11fa8ee 100644
--- a/third_party/blink/renderer/platform/graphics/DEPS
+++ b/third_party/blink/renderer/platform/graphics/DEPS
@@ -7,6 +7,7 @@
 
     # Dependencies.
     "+base/threading/sequenced_task_runner_handle.h",
+    "+base/threading/thread_restrictions.h",
     "+cc",
     "+components/viz/client",
     "+components/viz/common",
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
index 6a0023a..b5d35d00 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include "base/bind.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/trace_event/trace_event.h"
 #include "components/viz/client/client_resource_provider.h"
 #include "components/viz/common/gpu/context_provider.h"
@@ -92,7 +93,11 @@
       break;
   }
 
+  // When obtaining frame resources, we end up having to wait. See
+  // https://crbug/878070.
+  base::ScopedAllowBaseSyncPrimitives allow_base_sync_primitives;
   resource_updater_->ObtainFrameResources(frame);
+
   // TODO(lethalantidote) : update with true value;
   gfx::Rect visible_layer_rect = gfx::Rect(rotated_size);
   gfx::Rect clip_rect = gfx::Rect(frame->coded_size());
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
index b689026..7eda79e5 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
@@ -56,7 +56,6 @@
  private:
   const cc::LayerTreeSettings settings_;
 
-  WebContextProviderCallback context_provider_callback_;
   viz::ContextProvider* context_provider_;
   std::unique_ptr<viz::ClientResourceProvider> resource_provider_;
   std::unique_ptr<media::VideoResourceUpdater> resource_updater_;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index f45e670..e609f16 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -25,7 +25,8 @@
 namespace {
 
 // Delay to retry getting the context_provider.
-constexpr int kGetContextProviderRetryMS = 150;
+constexpr base::TimeDelta kGetContextProviderRetryTimeout =
+    base::TimeDelta::FromMilliseconds(150);
 
 }  // namespace
 
@@ -162,33 +163,45 @@
     DCHECK(!provider_);
     provider_ = provider;
     context_provider_callback_.Run(
-        base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
-                       weak_ptr_factory_.GetWeakPtr()));
+        nullptr, base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
+                                weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
 void VideoFrameSubmitter::OnReceivedContextProvider(
     bool use_gpu_compositing,
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider) {
-  // We could get a null |context_provider| back if the context is still lost.
-  if (!context_provider && use_gpu_compositing) {
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(
-            context_provider_callback_,
-            base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
-                           weak_ptr_factory_.GetWeakPtr())),
-        base::TimeDelta::FromMilliseconds(kGetContextProviderRetryMS));
+    scoped_refptr<viz::ContextProvider> context_provider) {
+  if (!use_gpu_compositing) {
+    resource_provider_->Initialize(nullptr, this);
     return;
   }
 
-  context_provider_ = std::move(context_provider);
-  if (use_gpu_compositing) {
-    context_provider_->AddObserver(this);
-    resource_provider_->Initialize(context_provider_.get(), nullptr);
-  } else {
-    resource_provider_->Initialize(nullptr, this);
+  bool has_good_context = false;
+  while (!has_good_context) {
+    if (!context_provider) {
+      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(
+              context_provider_callback_, context_provider_,
+              base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
+                             weak_ptr_factory_.GetWeakPtr())),
+          kGetContextProviderRetryTimeout);
+      return;
+    }
+
+    // Note that |context_provider| is now null after the move, such that if we
+    // end up having !|has_good_context|, we will retry to obtain the
+    // context_provider.
+    context_provider_ = std::move(context_provider);
+    auto result = context_provider_->BindToCurrentThread();
+
+    has_good_context =
+        result == gpu::ContextResult::kSuccess ||
+        context_provider_->ContextGL()->GetGraphicsResetStatusKHR() ==
+            GL_NO_ERROR;
   }
+  context_provider_->AddObserver(this);
+  resource_provider_->Initialize(context_provider_.get(), nullptr);
 
   if (frame_sink_id_.is_valid())
     StartSubmitting();
@@ -339,7 +352,6 @@
 
   if (context_provider_) {
     context_provider_->RemoveObserver(this);
-    context_provider_ = nullptr;
   }
   waiting_for_compositor_ack_ = false;
 
@@ -349,6 +361,7 @@
   compositor_frame_sink_.reset();
 
   context_provider_callback_.Run(
+      context_provider_,
       base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
                      weak_ptr_factory_.GetWeakPtr()));
 
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
index 6ef7e42..d138494 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
@@ -47,9 +47,7 @@
     return &binding_;
   }
 
-  void OnReceivedContextProvider(
-      bool,
-      scoped_refptr<ws::ContextProviderCommandBuffer>);
+  void OnReceivedContextProvider(bool, scoped_refptr<viz::ContextProvider>);
 
   // cc::VideoFrameProvider::Client implementation.
   void StopUsingProvider() override;
@@ -118,7 +116,7 @@
   bool ShouldSubmit() const;
 
   cc::VideoFrameProvider* provider_ = nullptr;
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
+  scoped_refptr<viz::ContextProvider> context_provider_;
   viz::mojom::blink::CompositorFrameSinkPtr compositor_frame_sink_;
   mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> binding_;
   WebContextProviderCallback context_provider_callback_;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
index f2d2eab..883e2ec 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -148,8 +148,9 @@
         context_provider_.get(), nullptr);
     submitter_ = std::make_unique<VideoFrameSubmitter>(
         base::BindRepeating(
-            [](base::OnceCallback<void(
-                   bool, scoped_refptr<ws::ContextProviderCommandBuffer>)>) {}),
+            [](scoped_refptr<viz::ContextProvider>,
+               base::OnceCallback<void(
+                   bool, scoped_refptr<viz::ContextProvider>)>) {}),
         base::WrapUnique<MockVideoFrameResourceProvider>(resource_provider_));
 
     submitter_->Initialize(provider_.get());