Bug 1449982 - Add the task queue for running things on the updater thread. r?botond draft
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 10 Apr 2018 12:29:55 -0400
changeset 779788 31efc2c2cb434895a782a3853ba333da96a67754
parent 779787 f79351207dd428b3e680d43e7a82c948cd5cf960
child 779789 2252362cadab2f26810a4fa743c6cc1066717ac3
push id105878
push userkgupta@mozilla.com
push dateTue, 10 Apr 2018 16:32:25 +0000
reviewersbotond
bugs1449982
milestone61.0a1
Bug 1449982 - Add the task queue for running things on the updater thread. r?botond If we're using the WR scene builder thread as the updater thread, we cannot just post a task to its message loop, because it's a rust thread that doesn't have a message loop. Instead, we put these tasks into a queue that we will process in callbacks that are invoked from the updater thread. This patch just adds the basic queue machinery, it will get more complicated in a future patch because we need to gate the tasks to make sure they don't run too early. MozReview-Commit-ID: 8DCYbsQ5cBC
gfx/layers/apz/public/APZUpdater.h
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/APZUpdater.cpp
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_APZUpdater_h
 #define mozilla_layers_APZUpdater_h
 
+#include <deque>
 #include <unordered_map>
 
 #include "base/platform_thread.h"   // for PlatformThreadId
 #include "LayersTypes.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/StaticMutex.h"
 #include "nsThreadUtils.h"
 #include "Units.h"
@@ -47,16 +48,17 @@ public:
 
   /**
    * This function is invoked from rust on the scene builder thread when it
    * is created. It effectively tells the APZUpdater "the current thread is
    * the updater thread for this window id" and allows APZUpdater to remember
    * which thread it is.
    */
   static void SetUpdaterThread(const wr::WrWindowId& aWindowId);
+  static void ProcessPendingTasks(const wr::WrWindowId& aWindowId);
 
   void ClearTree();
   void UpdateFocusState(LayersId aRootLayerTreeId,
                         LayersId aOriginatingLayersId,
                         const FocusTarget& aFocusTarget);
   void UpdateHitTestingTree(LayersId aRootLayerTreeId,
                             Layer* aRoot,
                             bool aIsFirstPaint,
@@ -135,14 +137,21 @@ private:
   // from multiple threads during normal operation (after initialization)
   // without locking should be fine.
   Maybe<PlatformThreadId> mUpdaterThreadId;
 #ifdef DEBUG
   // This flag is used to ensure that we don't ever try to do updater-thread
   // stuff before the updater thread has been properly initialized.
   mutable bool mUpdaterThreadQueried;
 #endif
+
+  // Lock used to protect mUpdaterQueue
+  Mutex mQueueLock;
+  // Holds a FIFO queue of tasks to be run on the updater thread,
+  // when the updater thread is a WebRender thread, since it won't have a
+  // message loop we can dispatch to.
+  std::deque<RefPtr<Runnable>> mUpdaterQueue;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_APZUpdater_h
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -528,16 +528,20 @@ public:
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
   // Assert that the current thread is the sampler thread for this APZCTM.
   void AssertOnSamplerThread();
   // Assert that the current thread is the updater thread for this APZCTM.
   void AssertOnUpdaterThread();
 
+  // Returns a pointer to the WebRenderAPI for the root layers id this APZCTreeManager
+  // is for. This might be null (for example, if WebRender is not enabled).
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
+
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
   APZSampler* GetSampler() const;
   APZUpdater* GetUpdater() const;
 
   // Protected hooks for gtests subclass
@@ -682,20 +686,16 @@ private:
                      const AsyncPanZoomController* apzc);
 
   void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const;
   void NotifyAutoscrollRejected(const ScrollableLayerGuid& aGuid) const;
 
   // Requires the caller to hold mTreeLock.
   LayerToParentLayerMatrix4x4 ComputeTransformForNode(const HitTestingTreeNode* aNode) const;
 
-  // Returns a pointer to the WebRenderAPI for the root layers id this APZCTreeManager
-  // is for. This might be null (for example, if WebRender is not enabled).
-  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
-
   // Returns a pointer to the GeckoContentController for the given layers id.
   already_AddRefed<GeckoContentController> GetContentController(LayersId aLayersId) const;
 
 protected:
   /* The input queue where input events are held until we know enough to
    * figure out where they're going. Protected so gtests can access it.
    */
   RefPtr<InputQueue> mInputQueue;
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -8,30 +8,32 @@
 
 #include "APZCTreeManager.h"
 #include "AsyncPanZoomController.h"
 #include "base/task.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/SynchronousTask.h"
 #include "mozilla/layers/WebRenderScrollData.h"
+#include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 StaticMutex APZUpdater::sWindowIdLock;
 std::unordered_map<uint64_t, APZUpdater*> APZUpdater::sWindowIdMap;
 
 
 APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz)
   : mApz(aApz)
 #ifdef DEBUG
   , mUpdaterThreadQueried(false)
 #endif
+  , mQueueLock("APZUpdater::QueueLock")
 {
   MOZ_ASSERT(aApz);
   mApz->SetUpdater(this);
 }
 
 APZUpdater::~APZUpdater()
 {
   mApz->SetUpdater(nullptr);
@@ -63,16 +65,28 @@ APZUpdater::SetUpdaterThread(const wr::W
 {
   if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
     // Ensure nobody tried to use the updater thread before this point.
     MOZ_ASSERT(!updater->mUpdaterThreadQueried);
     updater->mUpdaterThreadId = Some(PlatformThread::CurrentId());
   }
 }
 
+/*static*/ void
+APZUpdater::ProcessPendingTasks(const wr::WrWindowId& aWindowId)
+{
+  if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
+    MutexAutoLock lock(updater->mQueueLock);
+    for (auto task : updater->mUpdaterQueue) {
+      task->Run();
+    }
+    updater->mUpdaterQueue.clear();
+  }
+}
+
 void
 APZUpdater::ClearTree()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
   RunOnUpdaterThread(NS_NewRunnableFunction(
     "APZUpdater::ClearTree",
     [=]() {
@@ -249,18 +263,36 @@ APZUpdater::RunOnUpdaterThread(already_A
   RefPtr<Runnable> task = aTask;
 
   if (IsUpdaterThread()) {
     task->Run();
     return;
   }
 
   if (UsingWebRenderUpdaterThread()) {
-    // TODO
-    NS_WARNING("Dropping task posted to updater thread");
+    // If the updater thread is a WebRender thread, and we're not on it
+    // right now, save the task in the queue. We will run tasks from the queue
+    // during the callback from the updater thread, which we trigger by the
+    // call to WakeSceneBuilder.
+
+    { // scope lock
+      MutexAutoLock lock(mQueueLock);
+      mUpdaterQueue.push_back(task);
+    }
+    RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI();
+    if (api) {
+      api->WakeSceneBuilder();
+    } else {
+      // Not sure if this can happen, but it might be possible. If it does,
+      // the task is in the queue, but if we didn't get a WebRenderAPI it
+      // might never run, or it might run later if we manage to get a
+      // WebRenderAPI later. For now let's just emit a warning, this can
+      // probably be upgraded to an assert later.
+      NS_WARNING("Possibly dropping task posted to updater thread");
+    }
     return;
   }
 
   if (MessageLoop* loop = CompositorThreadHolder::Loop()) {
     loop->PostTask(task.forget());
   } else {
     // Could happen during startup
     NS_WARNING("Dropping task posted to updater thread");
@@ -339,14 +371,24 @@ void
 apz_post_scene_swap(mozilla::wr::WrWindowId aWindowId,
                     mozilla::wr::WrPipelineInfo* aInfo)
 {
 }
 
 void
 apz_run_updater(mozilla::wr::WrWindowId aWindowId)
 {
+  // This should never get called unless async scene building is enabled.
+  MOZ_ASSERT(gfxPrefs::WebRenderAsyncSceneBuild());
+  mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
 }
 
 void
 apz_deregister_updater(mozilla::wr::WrWindowId aWindowId)
 {
+  // Run anything that's still left. Note that this function gets called even
+  // if async scene building is off, but in that case we don't want to do
+  // anything (because the updater thread will be the compositor thread, and
+  // this will be called on the scene builder thread).
+  if (gfxPrefs::WebRenderAsyncSceneBuild()) {
+    mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
+  }
 }